1824 lines
55 KiB
JavaScript
1824 lines
55 KiB
JavaScript
// 【重要】必须使用 const Component 作为组件变量名
|
||
// 台账数据 - 车辆维修/保养明细(运维手动录入;提报人/类型多选筛选;确认提交后锁定,主管可编辑)
|
||
// 原型:保存/确认提交、表格内联编辑;联调后对接登录用户、角色与车辆清单接口
|
||
|
||
const Component = function () {
|
||
var useState = React.useState;
|
||
var useMemo = React.useMemo;
|
||
var useCallback = React.useCallback;
|
||
var useRef = React.useRef;
|
||
|
||
var antd = window.antd;
|
||
var App = antd.App;
|
||
var Breadcrumb = antd.Breadcrumb;
|
||
var Card = antd.Card;
|
||
var Button = antd.Button;
|
||
var Table = antd.Table;
|
||
var Select = antd.Select;
|
||
var DatePicker = antd.DatePicker;
|
||
var Input = antd.Input;
|
||
var InputNumber = antd.InputNumber;
|
||
var Row = antd.Row;
|
||
var Col = antd.Col;
|
||
var Space = antd.Space;
|
||
var Tag = antd.Tag;
|
||
var Popconfirm = antd.Popconfirm;
|
||
var Popover = antd.Popover;
|
||
var Tooltip = antd.Tooltip;
|
||
var Dropdown = antd.Dropdown;
|
||
var Modal = antd.Modal;
|
||
var message = antd.message;
|
||
|
||
var CURRENT_USER = { id: 'u_zhang', name: '张运维', role: 'staff' };
|
||
|
||
var SUBMIT_CONFIRM_TEXT = '提交后数据将无法修改,如需修改请联系运维主管进行修改';
|
||
|
||
var RECORD_TYPE_OPTIONS = [
|
||
{ value: '维修', label: '维修' },
|
||
{ value: '保养', label: '保养' }
|
||
];
|
||
|
||
var CHANGE_LOG_FIELD_LABELS = {
|
||
repairDate: '进修日期',
|
||
plateNo: '车牌号',
|
||
recordType: '类型',
|
||
itemDesc: '项目/材料',
|
||
laborUnit: '人工费单位',
|
||
laborQty: '人工费数量',
|
||
laborUnitPrice: '人工费单价(含税)',
|
||
partsUnit: '配件费单位',
|
||
partsQty: '配件费数量',
|
||
partsUnitPrice: '配件费单价(含税)',
|
||
submitStatus: '提交状态',
|
||
saveStatus: '保存状态',
|
||
_copy: '复制新增',
|
||
_create: '新增记录'
|
||
};
|
||
|
||
/**
|
||
* 系统全部车辆车牌(原型枚举,与车辆管理模块一致;联调后由车辆清单接口加载)
|
||
*/
|
||
var SYSTEM_VEHICLE_PLATES = [
|
||
'京C12345',
|
||
'京CN88771F',
|
||
'京E88888',
|
||
'京F33333',
|
||
'京G12345',
|
||
'京H88888',
|
||
'川AL55602F',
|
||
'沪A12345',
|
||
'沪AD12345F',
|
||
'沪B99999',
|
||
'沪C11111',
|
||
'沪D66666',
|
||
'浙AD12345F',
|
||
'浙AH55660F',
|
||
'浙BK33210F',
|
||
'苏EF99887F',
|
||
'粤A11B22',
|
||
'粤A12345',
|
||
'粤A66D66',
|
||
'粤A77F77',
|
||
'粤A88K88',
|
||
'粤A99A99',
|
||
'粤B12345',
|
||
'粤B22C33',
|
||
'粤B44E44',
|
||
'粤B55B55',
|
||
'粤B67890',
|
||
'粤BK33210F'
|
||
];
|
||
|
||
function normPlateKey(plate) {
|
||
return String(plate == null ? '' : plate)
|
||
.replace(/[\s\-·]/g, '')
|
||
.toUpperCase();
|
||
}
|
||
|
||
function isPlateInVehicleTable(plate) {
|
||
if (!plate) return false;
|
||
var key = normPlateKey(plate);
|
||
for (var i = 0; i < SYSTEM_VEHICLE_PLATES.length; i++) {
|
||
if (normPlateKey(SYSTEM_VEHICLE_PLATES[i]) === key) return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
function isEmptyStr(v) {
|
||
return !String(v == null ? '' : v).trim();
|
||
}
|
||
|
||
function isEmptyNum(v) {
|
||
return v === null || v === undefined || v === '';
|
||
}
|
||
|
||
function getRowInvalidMap(row) {
|
||
var inv = {};
|
||
if (!row.repairDate) inv.repairDate = true;
|
||
if (!row.plateNo) inv.plateNo = 'empty';
|
||
else if (!isPlateInVehicleTable(row.plateNo)) inv.plateNo = 'not_exist';
|
||
if (isEmptyStr(row.recordType)) inv.recordType = true;
|
||
if (isEmptyStr(row.itemDesc)) inv.itemDesc = true;
|
||
if (isEmptyStr(row.laborUnit)) inv.laborUnit = true;
|
||
if (isEmptyNum(row.laborQty)) inv.laborQty = true;
|
||
if (isEmptyNum(row.laborUnitPrice)) inv.laborUnitPrice = true;
|
||
if (isEmptyStr(row.partsUnit)) inv.partsUnit = true;
|
||
if (isEmptyNum(row.partsQty)) inv.partsQty = true;
|
||
if (isEmptyNum(row.partsUnitPrice)) inv.partsUnitPrice = true;
|
||
return inv;
|
||
}
|
||
|
||
/** 列表展示顺序:0 已确认提交 → 1 已保存 → 2 新增未保存 */
|
||
function rowDisplaySortTier(row) {
|
||
if (row && row.submitStatus === 'submitted') return 0;
|
||
if (row && row.saveStatus === 'saved') return 1;
|
||
return 2;
|
||
}
|
||
|
||
function rowStatusLabel(row) {
|
||
if (row && row.submitStatus === 'submitted') return '已提交';
|
||
if (row && row.saveStatus === 'saved') return '已保存';
|
||
return '待保存';
|
||
}
|
||
|
||
function isSavedDraftRow(row) {
|
||
return row && row.submitStatus !== 'submitted' && row.saveStatus === 'saved';
|
||
}
|
||
|
||
/** 状态为「已保存」的行均在操作列展示「编辑」「删除」 */
|
||
function canShowSavedActions(row) {
|
||
return isSavedDraftRow(row);
|
||
}
|
||
|
||
/** 状态为「已提交」:仅运维主管在操作列展示「编辑」「删除」 */
|
||
function canShowSubmittedActions(row, isSupervisorRole) {
|
||
return !!isSupervisorRole && row && row.submitStatus === 'submitted';
|
||
}
|
||
|
||
function canDeleteRow(row, isSupervisorRole) {
|
||
if (row && row.submitStatus === 'submitted') {
|
||
return !!isSupervisorRole;
|
||
}
|
||
if (isSupervisorRole) return true;
|
||
return row && row.createdBy === CURRENT_USER.id;
|
||
}
|
||
|
||
/** 已提交不可复制;其余本人或主管可复制 */
|
||
function canCopyRow(row, isSupervisorRole) {
|
||
if (!row || row.submitStatus === 'submitted') return false;
|
||
if (isSupervisorRole) return true;
|
||
return row.createdBy === CURRENT_USER.id;
|
||
}
|
||
|
||
function cloneRowTimeValue(t) {
|
||
if (!t) return t;
|
||
try {
|
||
if (window.dayjs && window.dayjs.isDayjs && window.dayjs.isDayjs(t)) return t.clone();
|
||
if (window.dayjs) {
|
||
var d = window.dayjs(t);
|
||
return d.isValid() ? d : t;
|
||
}
|
||
} catch (eClone) {}
|
||
return t;
|
||
}
|
||
|
||
function buildCopiedRow(sourceRow) {
|
||
var now = nowDayjs();
|
||
return recalcRow({
|
||
key: nextRowKey(),
|
||
createdBy: CURRENT_USER.id,
|
||
reporterName: CURRENT_USER.name,
|
||
repairDate: cloneRowTimeValue(sourceRow.repairDate),
|
||
plateNo: sourceRow.plateNo,
|
||
reportTime: now,
|
||
recordType: sourceRow.recordType,
|
||
itemDesc: sourceRow.itemDesc || '',
|
||
laborUnit: sourceRow.laborUnit || '',
|
||
laborQty: sourceRow.laborQty,
|
||
laborUnitPrice: sourceRow.laborUnitPrice,
|
||
laborAmount: 0,
|
||
partsUnit: sourceRow.partsUnit || '',
|
||
partsQty: sourceRow.partsQty,
|
||
partsUnitPrice: sourceRow.partsUnitPrice,
|
||
partsAmount: 0,
|
||
totalCost: 0,
|
||
submitStatus: 'draft',
|
||
saveStatus: 'unsaved',
|
||
submittedAt: null
|
||
});
|
||
}
|
||
|
||
function valuesEqualForLog(field, a, b) {
|
||
if (a === b) return true;
|
||
if ((a == null || a === '') && (b == null || b === '')) return true;
|
||
if (field === 'repairDate' && window.dayjs) {
|
||
try {
|
||
return window.dayjs(a).valueOf() === window.dayjs(b).valueOf();
|
||
} catch (eLog) {
|
||
return false;
|
||
}
|
||
}
|
||
if (
|
||
field === 'laborQty' ||
|
||
field === 'laborUnitPrice' ||
|
||
field === 'partsQty' ||
|
||
field === 'partsUnitPrice'
|
||
) {
|
||
if (isEmptyNum(a) && isEmptyNum(b)) return true;
|
||
if (field === 'laborQty' || field === 'partsQty') return toIntQty(a) === toIntQty(b);
|
||
return roundMoney(a) === roundMoney(b);
|
||
}
|
||
return String(a) === String(b);
|
||
}
|
||
|
||
function formatLogValue(field, value, rowCtx) {
|
||
if (value == null || value === '') return '—';
|
||
if (field === 'repairDate') return formatDate(value) || formatDateTime(value) || String(value);
|
||
if (field === 'submitStatus') {
|
||
if (value === 'submitted') return '已提交';
|
||
return '未提交';
|
||
}
|
||
if (field === 'saveStatus') {
|
||
if (value === 'saved') return '已保存';
|
||
return '待保存';
|
||
}
|
||
if (
|
||
field === 'laborQty' ||
|
||
field === 'partsQty'
|
||
) {
|
||
return isEmptyNum(value) ? '—' : String(toIntQty(value));
|
||
}
|
||
if (field === 'laborUnitPrice' || field === 'partsUnitPrice') {
|
||
return isEmptyNum(value) ? '—' : fmtMoney(value);
|
||
}
|
||
return String(value);
|
||
}
|
||
|
||
function rowTimeValue(row, field) {
|
||
var v = row && row[field];
|
||
if (!v || !window.dayjs) return 0;
|
||
try {
|
||
return window.dayjs(v).valueOf();
|
||
} catch (e1) {
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
function sortRowsWithinTier(rows) {
|
||
return rows.slice().sort(function (a, b) {
|
||
var ta = rowTimeValue(a, 'submittedAt') || rowTimeValue(a, 'reportTime');
|
||
var tb = rowTimeValue(b, 'submittedAt') || rowTimeValue(b, 'reportTime');
|
||
if (tb !== ta) return tb - ta;
|
||
return String(a.key || '').localeCompare(String(b.key || ''));
|
||
});
|
||
}
|
||
|
||
/** 分区合并,避免同层数据按日期与另一层穿插 */
|
||
function sortRowsByDisplayTier(rows) {
|
||
var submitted = [];
|
||
var saved = [];
|
||
var unsaved = [];
|
||
(rows || []).forEach(function (r) {
|
||
var tier = rowDisplaySortTier(r);
|
||
if (tier === 0) submitted.push(r);
|
||
else if (tier === 1) saved.push(r);
|
||
else unsaved.push(r);
|
||
});
|
||
return sortRowsWithinTier(submitted).concat(sortRowsWithinTier(saved)).concat(sortRowsWithinTier(unsaved));
|
||
}
|
||
|
||
function firstInvalidMessage(row, inv, seq) {
|
||
var prefix = '第' + seq + '行:';
|
||
if (inv.repairDate) return prefix + '请填写进修日期';
|
||
if (inv.plateNo === 'empty') return prefix + '请填写车牌号';
|
||
if (inv.plateNo === 'not_exist') return prefix + '车辆不存在';
|
||
if (inv.recordType) return prefix + '请选择类型';
|
||
if (inv.itemDesc) return prefix + '请填写项目/材料';
|
||
if (inv.laborUnit) return prefix + '请填写人工费单位';
|
||
if (inv.laborQty) return prefix + '请填写人工费数量';
|
||
if (inv.laborUnitPrice) return prefix + '请填写人工费单价(含税)';
|
||
if (inv.partsUnit) return prefix + '请填写配件费单位';
|
||
if (inv.partsQty) return prefix + '请填写配件费数量';
|
||
if (inv.partsUnitPrice) return prefix + '请填写配件费单价(含税)';
|
||
return prefix + '请完善必填项';
|
||
}
|
||
|
||
var OTHER_REPORTERS = [
|
||
{ id: 'u_zhou', name: '周敏' },
|
||
{ id: 'u_li_h', name: '李航' },
|
||
{ id: 'u_wang', name: '王磊' }
|
||
];
|
||
|
||
var rowIdSeed = 0;
|
||
function nextRowKey() {
|
||
rowIdSeed += 1;
|
||
return 'mr-' + Date.now() + '-' + rowIdSeed;
|
||
}
|
||
|
||
function filterOption(input, option) {
|
||
var label = (option && (option.label || option.children)) || '';
|
||
return String(label).toLowerCase().indexOf(String(input || '').toLowerCase()) >= 0;
|
||
}
|
||
|
||
/** 车牌号下拉:支持输入模糊匹配(忽略空格、大小写) */
|
||
function filterPlateOption(input, option) {
|
||
var q = normPlateKey(input);
|
||
if (!q) return true;
|
||
var label = normPlateKey(option && (option.label != null ? option.label : option.value));
|
||
return label.indexOf(q) >= 0;
|
||
}
|
||
|
||
function numOrZero(v) {
|
||
if (v === null || v === undefined || v === '') return 0;
|
||
var n = Number(v);
|
||
return isNaN(n) ? 0 : n;
|
||
}
|
||
|
||
function roundMoney(n) {
|
||
return Math.round(numOrZero(n) * 100) / 100;
|
||
}
|
||
|
||
function toIntQty(v) {
|
||
if (v === null || v === undefined || v === '') return 0;
|
||
var n = Math.floor(Number(v));
|
||
return isNaN(n) || n < 0 ? 0 : n;
|
||
}
|
||
|
||
function fmtMoney(n) {
|
||
if (n === null || n === undefined || n === '') return '0.00';
|
||
return roundMoney(n).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
||
}
|
||
|
||
function nowDayjs() {
|
||
try {
|
||
if (window.dayjs) return window.dayjs();
|
||
} catch (e1) {}
|
||
return null;
|
||
}
|
||
|
||
function formatDate(d) {
|
||
if (!d || !window.dayjs) return '';
|
||
try {
|
||
return window.dayjs(d).format('YYYY-MM-DD');
|
||
} catch (e2) {
|
||
return '';
|
||
}
|
||
}
|
||
|
||
function formatDateTime(d) {
|
||
if (!d || !window.dayjs) return '';
|
||
try {
|
||
return window.dayjs(d).format('YYYY-MM-DD HH:mm');
|
||
} catch (e3) {
|
||
return '';
|
||
}
|
||
}
|
||
|
||
function recalcRow(row) {
|
||
var laborQty = toIntQty(row.laborQty);
|
||
var partsQty = toIntQty(row.partsQty);
|
||
var laborAmt = roundMoney(laborQty * numOrZero(row.laborUnitPrice));
|
||
var partsAmt = roundMoney(partsQty * numOrZero(row.partsUnitPrice));
|
||
var total = roundMoney(laborAmt + partsAmt);
|
||
return Object.assign({}, row, {
|
||
laborQty: laborQty,
|
||
partsQty: partsQty,
|
||
laborAmount: laborAmt,
|
||
partsAmount: partsAmt,
|
||
totalCost: total
|
||
});
|
||
}
|
||
|
||
function buildEmptyRow(user) {
|
||
var now = nowDayjs();
|
||
return recalcRow({
|
||
key: nextRowKey(),
|
||
createdBy: user.id,
|
||
reporterName: user.name,
|
||
repairDate: now,
|
||
plateNo: undefined,
|
||
reportTime: now,
|
||
recordType: undefined,
|
||
itemDesc: '',
|
||
laborUnit: '',
|
||
laborQty: null,
|
||
laborUnitPrice: null,
|
||
laborAmount: 0,
|
||
partsUnit: '',
|
||
partsQty: null,
|
||
partsUnitPrice: null,
|
||
partsAmount: 0,
|
||
totalCost: 0,
|
||
submitStatus: 'draft',
|
||
saveStatus: 'unsaved'
|
||
});
|
||
}
|
||
|
||
function buildMockAllRows() {
|
||
var plates = SYSTEM_VEHICLE_PLATES;
|
||
var items = ['更换空压机滤芯', '制动片检修', '冷却液补充', '高压线束检查', '轮胎换位'];
|
||
var rows = [];
|
||
var reporters = [{ id: CURRENT_USER.id, name: CURRENT_USER.name }].concat(OTHER_REPORTERS);
|
||
var i;
|
||
for (i = 0; i < 8; i++) {
|
||
var rep = reporters[i % reporters.length];
|
||
var submitStatus = 'draft';
|
||
var saveStatus = 'unsaved';
|
||
if (rep.id === CURRENT_USER.id) {
|
||
if (i < 3) {
|
||
submitStatus = 'submitted';
|
||
saveStatus = 'saved';
|
||
} else if (i === 3) {
|
||
saveStatus = 'saved';
|
||
}
|
||
} else {
|
||
saveStatus = 'saved';
|
||
}
|
||
var laborQty = 1 + (i % 4);
|
||
var laborPrice = 120 + i * 15;
|
||
var partsQty = i % 2 === 0 ? 2 : 0;
|
||
var partsPrice = 80 + i * 20;
|
||
var d = nowDayjs();
|
||
var repairDay = d && d.subtract ? d.subtract(i, 'day') : d;
|
||
rows.push(
|
||
recalcRow({
|
||
key: 'mock-' + (i + 1),
|
||
createdBy: rep.id,
|
||
reporterName: rep.name,
|
||
repairDate: repairDay,
|
||
plateNo: plates[i % plates.length],
|
||
reportTime: repairDay,
|
||
recordType: i % 3 === 0 ? '保养' : '维修',
|
||
itemDesc: items[i % items.length],
|
||
laborUnit: '工时',
|
||
laborQty: laborQty,
|
||
laborUnitPrice: laborPrice,
|
||
laborAmount: 0,
|
||
partsUnit: '件',
|
||
partsQty: partsQty,
|
||
partsUnitPrice: partsPrice,
|
||
partsAmount: 0,
|
||
totalCost: 0,
|
||
submitStatus: submitStatus,
|
||
saveStatus: saveStatus,
|
||
submittedAt: submitStatus === 'submitted' ? repairDay : null
|
||
})
|
||
);
|
||
}
|
||
return rows;
|
||
}
|
||
|
||
var layoutStyle = {
|
||
padding: '16px 24px 24px',
|
||
minHeight: '100vh',
|
||
background: 'linear-gradient(165deg, #eef4ff 0%, #f5f7fa 42%, #f0f2f5 100%)'
|
||
};
|
||
var filterLabelStyle = { marginBottom: 6, fontSize: 13, color: 'rgba(0,0,0,0.55)', fontWeight: 500 };
|
||
var filterItemStyle = { marginBottom: 12 };
|
||
var filterControlStyle = { width: '100%' };
|
||
var filterActionsColStyle = { flex: '0 0 auto', marginLeft: 'auto' };
|
||
|
||
var filterCardStyle = {
|
||
marginBottom: 20,
|
||
borderRadius: 16,
|
||
boxShadow: '0 4px 20px -4px rgba(16,24,40,0.03), 0 0 0 1px rgba(16,24,40,0.06)',
|
||
border: 'none',
|
||
background: '#ffffff'
|
||
};
|
||
|
||
var tableCardStyle = {
|
||
borderRadius: 16,
|
||
boxShadow: '0 10px 32px -4px rgba(16,24,40,0.06), 0 0 0 1px rgba(16,24,40,0.04)',
|
||
border: 'none',
|
||
background: '#ffffff',
|
||
overflow: 'hidden'
|
||
};
|
||
|
||
var ledgerTableStyle =
|
||
'.maint-ledger-table-wrap{border-radius:12px;overflow:hidden;box-shadow:0 4px 24px -6px rgba(15,23,42,0.05),0 0 0 1px rgba(22,119,255,0.1)}' +
|
||
'.maint-ledger-table .ant-table-thead>tr>th,.maint-ledger-table .ant-table-thead .ant-table-cell{white-space:nowrap;color:#0f172a!important;font-weight:600!important;font-size:13px!important;' +
|
||
'background:#e8f4fc!important;border-bottom:1px solid #bae6fd!important;border-inline-end:1px solid #dbeafe!important;padding:0 8px!important;height:38px!important;text-align:center!important}' +
|
||
'.maint-ledger-table .ant-table-thead>tr:not(:last-child)>th{border-bottom:1px solid #bae6fd!important}' +
|
||
'.maint-repair-date-picker,.maint-repair-date-picker .ant-picker-input{width:100%;min-width:132px}' +
|
||
'.maint-repair-date-picker .ant-picker-input>input{min-width:118px}' +
|
||
'.maint-ledger-table .ant-table-tbody>tr:not(.ant-table-measure-row)>td{padding:4px 6px!important;vertical-align:middle!important}' +
|
||
'.maint-ledger-table .ant-table-tbody>tr.maint-row-data:hover>td{background:#f0f9ff!important}' +
|
||
'.maint-ledger-table .ant-table-summary>tr>td{font-weight:700;background:#f8fafc!important;color:#0f172a!important;border-top:2px solid #cbd5e1!important;padding:0 8px!important;height:38px!important}' +
|
||
'.maint-cell-readonly{color:#64748b;background:#f8fafc}' +
|
||
'.maint-inline-input{width:100%}' +
|
||
'.maint-row-tier-0>td{background:#f8fafc!important}' +
|
||
'.maint-row-tier-1>td{background:#fff!important}' +
|
||
'.maint-row-tier-2>td{background:#fffbeb!important}' +
|
||
'.maint-row-tier-boundary>td{border-top:2px solid #7dd3fc!important}' +
|
||
'.maint-action-icon-btn{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;border-radius:4px;cursor:pointer;color:rgba(15,23,42,0.55);transition:background .15s,color .15s}' +
|
||
'.maint-action-icon-btn:hover{background:#f0f9ff;color:#1677ff}' +
|
||
'.maint-action-icon-btn.maint-action-icon-danger:hover{background:#fff1f0;color:#ff4d4f}' +
|
||
'.maint-action-icon-btn.is-disabled{color:rgba(15,23,42,0.25);cursor:not-allowed;pointer-events:none}' +
|
||
'.maint-row-more-btn.maint-action-icon-btn:hover{background:#f5f5f5;color:rgba(15,23,42,0.75)}';
|
||
|
||
var rowsState = useState(buildMockAllRows);
|
||
var allRows = rowsState[0];
|
||
var setAllRows = rowsState[1];
|
||
|
||
var vehiclePlateOptions = useMemo(function () {
|
||
return SYSTEM_VEHICLE_PLATES.map(function (p) {
|
||
return { value: p, label: p };
|
||
});
|
||
}, []);
|
||
|
||
var plateDraftState = useState(undefined);
|
||
var plateDraft = plateDraftState[0];
|
||
var setPlateDraft = plateDraftState[1];
|
||
|
||
/** 提报人多选:空数组表示全部 */
|
||
var reporterDraftState = useState([]);
|
||
var reporterDraft = reporterDraftState[0];
|
||
var setReporterDraft = reporterDraftState[1];
|
||
|
||
var dateRangeDraftState = useState(null);
|
||
var dateRangeDraft = dateRangeDraftState[0];
|
||
var setDateRangeDraft = dateRangeDraftState[1];
|
||
|
||
/** 类型多选:空数组表示全部(保养、维修) */
|
||
var typeDraftState = useState([]);
|
||
var typeDraft = typeDraftState[0];
|
||
var setTypeDraft = typeDraftState[1];
|
||
|
||
var plateAppliedState = useState(undefined);
|
||
var plateApplied = plateAppliedState[0];
|
||
var setPlateApplied = plateAppliedState[1];
|
||
|
||
var reporterAppliedState = useState([]);
|
||
var reporterApplied = reporterAppliedState[0];
|
||
var setReporterApplied = reporterAppliedState[1];
|
||
|
||
var dateRangeAppliedState = useState(null);
|
||
var dateRangeApplied = dateRangeAppliedState[0];
|
||
var setDateRangeApplied = dateRangeAppliedState[1];
|
||
|
||
var typeAppliedState = useState([]);
|
||
var typeApplied = typeAppliedState[0];
|
||
var setTypeApplied = typeAppliedState[1];
|
||
|
||
/** 联调后由账号权限判断是否运维主管 */
|
||
var isSupervisor = false;
|
||
var editingKeysState = useState([]);
|
||
var editingKeys = editingKeysState[0];
|
||
var setEditingKeys = editingKeysState[1];
|
||
|
||
var rowInvalidState = useState({});
|
||
var rowInvalid = rowInvalidState[0];
|
||
var setRowInvalid = rowInvalidState[1];
|
||
|
||
var changeLogsByKeyState = useState({});
|
||
var changeLogsByKey = changeLogsByKeyState[0];
|
||
var setChangeLogsByKey = changeLogsByKeyState[1];
|
||
|
||
var changeLogModalState = useState({ open: false, rowKey: null, rowLabel: '' });
|
||
var changeLogModal = changeLogModalState[0];
|
||
var setChangeLogModal = changeLogModalState[1];
|
||
|
||
var changeLogsSeededRef = useRef(false);
|
||
|
||
var appendChangeLog = useCallback(function (rowKey, field, before, after, rowCtx, meta) {
|
||
if (!rowKey) return;
|
||
meta = meta || {};
|
||
var fieldLabel = meta.fieldLabel || CHANGE_LOG_FIELD_LABELS[field] || field;
|
||
var beforeText = meta.beforeText != null ? meta.beforeText : formatLogValue(field, before, rowCtx);
|
||
var afterText = meta.afterText != null ? meta.afterText : formatLogValue(field, after, rowCtx);
|
||
if (!meta.force && beforeText === afterText) return;
|
||
setChangeLogsByKey(function (prev) {
|
||
var list = (prev[rowKey] || []).slice();
|
||
list.unshift({
|
||
id: 'clog-' + Date.now() + '-' + Math.random().toString(36).slice(2, 8),
|
||
at: nowDayjs(),
|
||
userId: CURRENT_USER.id,
|
||
userName: CURRENT_USER.name,
|
||
field: field,
|
||
fieldLabel: fieldLabel,
|
||
before: beforeText,
|
||
after: afterText
|
||
});
|
||
var next = Object.assign({}, prev);
|
||
next[rowKey] = list;
|
||
return next;
|
||
});
|
||
}, []);
|
||
|
||
var openChangeLogModal = useCallback(function (record) {
|
||
if (!record) return;
|
||
var label = (record.plateNo || '') + (record.repairDate ? ' · ' + formatDate(record.repairDate) : '');
|
||
setChangeLogModal({
|
||
open: true,
|
||
rowKey: record.key,
|
||
rowLabel: label.trim()
|
||
});
|
||
}, []);
|
||
|
||
var closeChangeLogModal = useCallback(function () {
|
||
setChangeLogModal({ open: false, rowKey: null, rowLabel: '' });
|
||
}, []);
|
||
|
||
var copyPopoverKeyState = useState(null);
|
||
var copyPopoverKey = copyPopoverKeyState[0];
|
||
var setCopyPopoverKey = copyPopoverKeyState[1];
|
||
var copyRowCountState = useState(1);
|
||
var copyRowCount = copyRowCountState[0];
|
||
var setCopyRowCount = copyRowCountState[1];
|
||
|
||
var applyCopyRows = useCallback(function (sourceKey, count) {
|
||
var n = Math.floor(Number(count));
|
||
if (!isFinite(n) || n < 1) {
|
||
message.warning('请输入大于0的复制行数');
|
||
return;
|
||
}
|
||
if (n > 100) {
|
||
message.warning('单次最多复制100行');
|
||
return;
|
||
}
|
||
var didCopy = false;
|
||
setAllRows(function (prev) {
|
||
var source = null;
|
||
var i;
|
||
for (i = 0; i < prev.length; i++) {
|
||
if (prev[i].key === sourceKey) {
|
||
source = prev[i];
|
||
break;
|
||
}
|
||
}
|
||
if (!source || !canCopyRow(source, isSupervisor)) return prev;
|
||
var copies = [];
|
||
for (i = 0; i < n; i++) {
|
||
copies.push(buildCopiedRow(source));
|
||
}
|
||
var ci;
|
||
var sourceLabel = (source.plateNo || '当前行') + (source.itemDesc ? ' · ' + source.itemDesc : '');
|
||
for (ci = 0; ci < copies.length; ci++) {
|
||
appendChangeLog(copies[ci].key, '_copy', null, null, source, {
|
||
fieldLabel: '复制新增',
|
||
beforeText: '—',
|
||
afterText: '复制自「' + sourceLabel + '」',
|
||
force: true
|
||
});
|
||
}
|
||
didCopy = true;
|
||
return prev.concat(copies);
|
||
});
|
||
setCopyPopoverKey(null);
|
||
if (didCopy) message.success('已复制 ' + n + ' 行');
|
||
else message.warning('无法复制该条记录');
|
||
}, [appendChangeLog, isSupervisor]);
|
||
|
||
var latestRefs = useRef({});
|
||
latestRefs.current = {
|
||
setAllRows: setAllRows,
|
||
setEditingKeys: setEditingKeys,
|
||
setRowInvalid: setRowInvalid,
|
||
editingKeys: editingKeys,
|
||
rowInvalid: rowInvalid,
|
||
isSupervisor: isSupervisor,
|
||
vehicleOptions: vehiclePlateOptions,
|
||
appendChangeLog: appendChangeLog
|
||
};
|
||
|
||
React.useEffect(function () {
|
||
if (changeLogsSeededRef.current) return;
|
||
changeLogsSeededRef.current = true;
|
||
var demoAt = nowDayjs();
|
||
var demoAt2 = demoAt && demoAt.subtract ? demoAt.subtract(2, 'hour') : demoAt;
|
||
setChangeLogsByKey({
|
||
'mock-1': [
|
||
{
|
||
id: 'clog-demo-2',
|
||
at: demoAt,
|
||
userId: CURRENT_USER.id,
|
||
userName: CURRENT_USER.name,
|
||
field: 'itemDesc',
|
||
fieldLabel: '项目/材料',
|
||
before: '更换空压机滤芯',
|
||
after: '制动片检修'
|
||
},
|
||
{
|
||
id: 'clog-demo-1',
|
||
at: demoAt2,
|
||
userId: CURRENT_USER.id,
|
||
userName: CURRENT_USER.name,
|
||
field: 'recordType',
|
||
fieldLabel: '类型',
|
||
before: '维修',
|
||
after: '保养'
|
||
}
|
||
]
|
||
});
|
||
}, []);
|
||
|
||
var isRowSubmitted = useCallback(function (row) {
|
||
return row && row.submitStatus === 'submitted';
|
||
}, []);
|
||
|
||
var isRowSaved = useCallback(function (row) {
|
||
return row && row.saveStatus === 'saved';
|
||
}, []);
|
||
|
||
/** 是否可编辑单元格:未保存草稿直接编辑;已保存需点「编辑」;已提交仅运维主管点「编辑」后可改 */
|
||
var canEditRow = useCallback(
|
||
function (row) {
|
||
if (!row) return false;
|
||
if (isRowSubmitted(row)) {
|
||
if (!isSupervisor) return false;
|
||
return editingKeys.indexOf(row.key) >= 0;
|
||
}
|
||
if (isSavedDraftRow(row)) {
|
||
if (isSupervisor) return editingKeys.indexOf(row.key) >= 0;
|
||
if (row.createdBy !== CURRENT_USER.id) return false;
|
||
return editingKeys.indexOf(row.key) >= 0;
|
||
}
|
||
if (isSupervisor) return false;
|
||
if (row.createdBy !== CURRENT_USER.id) return false;
|
||
return true;
|
||
},
|
||
[isSupervisor, editingKeys, isRowSubmitted]
|
||
);
|
||
|
||
var visibleRows = useMemo(function () {
|
||
var list = allRows.slice();
|
||
if (plateApplied) {
|
||
list = list.filter(function (r) { return r.plateNo === plateApplied; });
|
||
}
|
||
if (reporterApplied && reporterApplied.length > 0) {
|
||
list = list.filter(function (r) { return reporterApplied.indexOf(r.reporterName) >= 0; });
|
||
}
|
||
if (typeApplied && typeApplied.length > 0) {
|
||
list = list.filter(function (r) { return typeApplied.indexOf(r.recordType) >= 0; });
|
||
}
|
||
if (dateRangeApplied && dateRangeApplied[0] && dateRangeApplied[1] && window.dayjs) {
|
||
var start = window.dayjs(dateRangeApplied[0]).startOf('day');
|
||
var end = window.dayjs(dateRangeApplied[1]).endOf('day');
|
||
list = list.filter(function (r) {
|
||
if (!r.repairDate) return false;
|
||
var d = window.dayjs(r.repairDate);
|
||
return d.isAfter(start.subtract(1, 'ms')) && d.isBefore(end.add(1, 'ms'));
|
||
});
|
||
}
|
||
list = sortRowsByDisplayTier(list);
|
||
return list.map(function (r, idx) {
|
||
return Object.assign({}, r, { seq: idx + 1, displayTier: rowDisplaySortTier(r) });
|
||
});
|
||
}, [allRows, plateApplied, reporterApplied, typeApplied, dateRangeApplied]);
|
||
|
||
/** 合计行仅汇总三个人含税金额字段 */
|
||
var totals = useMemo(function () {
|
||
return visibleRows.reduce(
|
||
function (acc, r) {
|
||
acc.laborAmount += numOrZero(r.laborAmount);
|
||
acc.partsAmount += numOrZero(r.partsAmount);
|
||
acc.totalCost += numOrZero(r.totalCost);
|
||
return acc;
|
||
},
|
||
{ laborAmount: 0, partsAmount: 0, totalCost: 0 }
|
||
);
|
||
}, [visibleRows]);
|
||
|
||
var reporterOptions = useMemo(function () {
|
||
var names = {};
|
||
names[CURRENT_USER.name] = true;
|
||
OTHER_REPORTERS.forEach(function (rep) {
|
||
if (rep.name) names[rep.name] = true;
|
||
});
|
||
allRows.forEach(function (r) {
|
||
if (r.reporterName) names[r.reporterName] = true;
|
||
});
|
||
return Object.keys(names).map(function (n) { return { value: n, label: n }; });
|
||
}, [allRows]);
|
||
|
||
var updateRow = useCallback(function (key, patch) {
|
||
setAllRows(function (prev) {
|
||
return prev.map(function (r) {
|
||
if (r.key !== key) return r;
|
||
return recalcRow(Object.assign({}, r, patch));
|
||
});
|
||
});
|
||
}, []);
|
||
|
||
var addRow = useCallback(function () {
|
||
var row = buildEmptyRow(CURRENT_USER);
|
||
appendChangeLog(row.key, '_create', null, null, row, {
|
||
fieldLabel: '新增记录',
|
||
beforeText: '—',
|
||
afterText: '新增空白行',
|
||
force: true
|
||
});
|
||
setAllRows(function (prev) {
|
||
return prev.concat([row]);
|
||
});
|
||
message.success('已新增一行,请填写维修/保养信息');
|
||
}, [appendChangeLog]);
|
||
|
||
var handleQuery = useCallback(function () {
|
||
setPlateApplied(plateDraft);
|
||
setReporterApplied(reporterDraft);
|
||
setTypeApplied(typeDraft);
|
||
setDateRangeApplied(dateRangeDraft);
|
||
message.success('查询成功');
|
||
}, [plateDraft, reporterDraft, typeDraft, dateRangeDraft]);
|
||
|
||
var handleReset = useCallback(function () {
|
||
setPlateDraft(undefined);
|
||
setReporterDraft([]);
|
||
setTypeDraft([]);
|
||
setDateRangeDraft(null);
|
||
setPlateApplied(undefined);
|
||
setReporterApplied([]);
|
||
setTypeApplied([]);
|
||
setDateRangeApplied(null);
|
||
}, []);
|
||
|
||
function validateMaintainableRows(rows) {
|
||
var allInvalid = {};
|
||
var firstMsg = '';
|
||
(rows || []).forEach(function (r, idx) {
|
||
var inv = getRowInvalidMap(r);
|
||
if (Object.keys(inv).length > 0) {
|
||
allInvalid[r.key] = inv;
|
||
if (!firstMsg) firstMsg = firstInvalidMessage(r, inv, r.seq || idx + 1);
|
||
}
|
||
});
|
||
if (firstMsg) {
|
||
setRowInvalid(allInvalid);
|
||
message.warning(firstMsg);
|
||
return false;
|
||
}
|
||
setRowInvalid({});
|
||
return true;
|
||
}
|
||
|
||
var pendingSubmitRows = useMemo(function () {
|
||
return allRows.filter(function (r) {
|
||
return r.createdBy === CURRENT_USER.id && r.submitStatus !== 'submitted' && r.saveStatus === 'saved';
|
||
});
|
||
}, [allRows]);
|
||
|
||
var handleSave = useCallback(function () {
|
||
var toSave = allRows.filter(function (r) {
|
||
return r.createdBy === CURRENT_USER.id && r.submitStatus !== 'submitted';
|
||
});
|
||
if (!toSave.length) {
|
||
message.info('暂无待保存的草稿记录');
|
||
return;
|
||
}
|
||
if (!validateMaintainableRows(toSave)) return;
|
||
setAllRows(function (prev) {
|
||
return prev.map(function (r) {
|
||
if (r.createdBy === CURRENT_USER.id && r.submitStatus !== 'submitted') {
|
||
return Object.assign({}, r, { saveStatus: 'saved' });
|
||
}
|
||
return r;
|
||
});
|
||
});
|
||
setEditingKeys([]);
|
||
message.success('保存成功(原型)');
|
||
}, [allRows]);
|
||
|
||
var handleConfirmSubmit = useCallback(function () {
|
||
if (isSupervisor) {
|
||
message.info('运维主管无需提交,请使用编辑修改已提交记录');
|
||
return;
|
||
}
|
||
if (!pendingSubmitRows.length) {
|
||
var hasUnsaved = allRows.some(function (r) {
|
||
return r.createdBy === CURRENT_USER.id && r.submitStatus !== 'submitted' && r.saveStatus !== 'saved';
|
||
});
|
||
message.info(hasUnsaved ? '请先保存后再确认提交' : '暂无待提交的已保存记录');
|
||
return;
|
||
}
|
||
if (!validateMaintainableRows(pendingSubmitRows)) return;
|
||
Modal.confirm({
|
||
title: '确认提交',
|
||
content: SUBMIT_CONFIRM_TEXT,
|
||
okText: '确认',
|
||
cancelText: '取消',
|
||
onOk: function () {
|
||
setAllRows(function (prev) {
|
||
return prev.map(function (r) {
|
||
if (r.createdBy === CURRENT_USER.id && r.submitStatus !== 'submitted' && r.saveStatus === 'saved') {
|
||
if (!valuesEqualForLog('submitStatus', r.submitStatus, 'submitted')) {
|
||
appendChangeLog(r.key, 'submitStatus', r.submitStatus, 'submitted', r, { force: true });
|
||
}
|
||
return Object.assign({}, r, {
|
||
submitStatus: 'submitted',
|
||
saveStatus: 'saved',
|
||
submittedAt: nowDayjs()
|
||
});
|
||
}
|
||
return r;
|
||
});
|
||
});
|
||
setEditingKeys([]);
|
||
message.success('提交成功,数据已锁定');
|
||
}
|
||
});
|
||
}, [isSupervisor, pendingSubmitRows, allRows, appendChangeLog]);
|
||
|
||
var cellInputStyle = { width: '100%' };
|
||
var cellNumStyle = { width: '100%' };
|
||
|
||
var columns = useMemo(function () {
|
||
function patch(key, field, val) {
|
||
var ref = latestRefs.current;
|
||
var patchObj = {};
|
||
patchObj[field] = val;
|
||
ref.setAllRows(function (prev) {
|
||
return prev.map(function (r) {
|
||
if (r.key !== key) return r;
|
||
var next = recalcRow(Object.assign({}, r, patchObj));
|
||
if (ref.appendChangeLog && !valuesEqualForLog(field, r[field], val)) {
|
||
ref.appendChangeLog(key, field, r[field], val, next);
|
||
}
|
||
return next;
|
||
});
|
||
});
|
||
ref.setRowInvalid(function (prev) {
|
||
if (!prev || !prev[key] || !prev[key][field]) return prev;
|
||
var next = Object.assign({}, prev);
|
||
var rowInv = Object.assign({}, next[key]);
|
||
delete rowInv[field];
|
||
if (Object.keys(rowInv).length === 0) {
|
||
delete next[key];
|
||
} else {
|
||
next[key] = rowInv;
|
||
}
|
||
return next;
|
||
});
|
||
}
|
||
|
||
function readOnlyCell(v, align) {
|
||
return React.createElement(
|
||
'div',
|
||
{
|
||
className: 'maint-cell-readonly',
|
||
style: { padding: '4px 8px', textAlign: align || 'center' }
|
||
},
|
||
v === 0 || v === '0' ? v : v || '-'
|
||
);
|
||
}
|
||
|
||
function fieldInvalid(record, field) {
|
||
var ref = latestRefs.current;
|
||
var rowInv = (ref.rowInvalid && ref.rowInvalid[record.key]) || {};
|
||
return rowInv[field];
|
||
}
|
||
|
||
function reqTitle(text) {
|
||
return React.createElement(
|
||
'span',
|
||
null,
|
||
React.createElement('span', { style: { color: '#ff4d4f', marginRight: 2 } }, '*'),
|
||
text
|
||
);
|
||
}
|
||
|
||
return [
|
||
{
|
||
title: '序号',
|
||
dataIndex: 'seq',
|
||
key: 'seq',
|
||
width: 56,
|
||
align: 'center',
|
||
fixed: 'left'
|
||
},
|
||
{
|
||
title: '状态',
|
||
dataIndex: 'displayTier',
|
||
key: 'recordStatus',
|
||
width: 76,
|
||
align: 'center',
|
||
fixed: 'left',
|
||
render: function (_, record) {
|
||
var label = rowStatusLabel(record);
|
||
var color = label === '已提交' ? 'blue' : label === '已保存' ? 'orange' : 'default';
|
||
return React.createElement(Tag, { color: color, style: { margin: 0 } }, label);
|
||
}
|
||
},
|
||
{
|
||
title: reqTitle('进修日期'),
|
||
dataIndex: 'repairDate',
|
||
key: 'repairDate',
|
||
width: 152,
|
||
align: 'center',
|
||
render: function (v, record) {
|
||
if (!canEditRow(record)) return readOnlyCell(formatDate(v));
|
||
return React.createElement(DatePicker, {
|
||
className: 'maint-repair-date-picker',
|
||
style: { width: '100%', minWidth: 132 },
|
||
format: 'YYYY-MM-DD',
|
||
placeholder: '请选择进修日期',
|
||
allowClear: true,
|
||
value: v,
|
||
status: fieldInvalid(record, 'repairDate') ? 'error' : undefined,
|
||
onChange: function (d) { patch(record.key, 'repairDate', d); }
|
||
});
|
||
}
|
||
},
|
||
{
|
||
title: reqTitle('车牌号'),
|
||
dataIndex: 'plateNo',
|
||
key: 'plateNo',
|
||
width: 140,
|
||
align: 'center',
|
||
render: function (v, record) {
|
||
var ref = latestRefs.current;
|
||
if (!canEditRow(record)) return readOnlyCell(v);
|
||
var plateInv = fieldInvalid(record, 'plateNo');
|
||
return React.createElement(Select, {
|
||
style: cellInputStyle,
|
||
showSearch: true,
|
||
allowClear: true,
|
||
placeholder: '请输入车牌号搜索',
|
||
value: v,
|
||
status: plateInv ? 'error' : undefined,
|
||
options: ref.vehicleOptions,
|
||
optionFilterProp: 'label',
|
||
filterOption: filterPlateOption,
|
||
notFoundContent: '未找到匹配车辆',
|
||
onChange: function (val) { patch(record.key, 'plateNo', val); }
|
||
});
|
||
}
|
||
},
|
||
{
|
||
title: '提报人',
|
||
dataIndex: 'reporterName',
|
||
key: 'reporterName',
|
||
width: 88,
|
||
align: 'center',
|
||
render: function (v) { return readOnlyCell(v); }
|
||
},
|
||
{
|
||
title: '提报时间',
|
||
dataIndex: 'reportTime',
|
||
key: 'reportTime',
|
||
width: 168,
|
||
align: 'center',
|
||
render: function (v) {
|
||
return React.createElement(
|
||
'div',
|
||
{ className: 'maint-cell-readonly', style: { padding: '4px 8px', textAlign: 'center', whiteSpace: 'nowrap', minWidth: 152 } },
|
||
formatDateTime(v) || '-'
|
||
);
|
||
}
|
||
},
|
||
{
|
||
title: reqTitle('类型'),
|
||
dataIndex: 'recordType',
|
||
key: 'recordType',
|
||
width: 100,
|
||
align: 'center',
|
||
render: function (v, record) {
|
||
if (!canEditRow(record)) return readOnlyCell(v);
|
||
return React.createElement(Select, {
|
||
style: cellInputStyle,
|
||
allowClear: true,
|
||
placeholder: '请选择类型',
|
||
value: v,
|
||
status: fieldInvalid(record, 'recordType') ? 'error' : undefined,
|
||
options: RECORD_TYPE_OPTIONS,
|
||
onChange: function (val) { patch(record.key, 'recordType', val); }
|
||
});
|
||
}
|
||
},
|
||
{
|
||
title: reqTitle('项目/材料'),
|
||
dataIndex: 'itemDesc',
|
||
key: 'itemDesc',
|
||
width: 160,
|
||
align: 'center',
|
||
render: function (v, record) {
|
||
if (!canEditRow(record)) return readOnlyCell(v);
|
||
return React.createElement(Input, {
|
||
className: 'maint-inline-input',
|
||
value: v,
|
||
placeholder: '请输入项目/材料',
|
||
status: fieldInvalid(record, 'itemDesc') ? 'error' : undefined,
|
||
onChange: function (e) { patch(record.key, 'itemDesc', e.target.value); }
|
||
});
|
||
}
|
||
},
|
||
{
|
||
title: '人工费',
|
||
key: 'laborGroup',
|
||
children: [
|
||
{
|
||
title: reqTitle('单位'),
|
||
dataIndex: 'laborUnit',
|
||
key: 'laborUnit',
|
||
width: 72,
|
||
align: 'center',
|
||
render: function (v, record) {
|
||
if (!canEditRow(record)) return readOnlyCell(v);
|
||
return React.createElement(Input, {
|
||
className: 'maint-inline-input',
|
||
value: v,
|
||
placeholder: '单位',
|
||
status: fieldInvalid(record, 'laborUnit') ? 'error' : undefined,
|
||
onChange: function (e) { patch(record.key, 'laborUnit', e.target.value); }
|
||
});
|
||
}
|
||
},
|
||
{
|
||
title: reqTitle('数量'),
|
||
dataIndex: 'laborQty',
|
||
key: 'laborQty',
|
||
width: 88,
|
||
align: 'right',
|
||
render: function (v, record) {
|
||
if (!canEditRow(record)) return readOnlyCell(toIntQty(v), 'right');
|
||
return React.createElement(InputNumber, {
|
||
style: cellNumStyle,
|
||
min: 0,
|
||
step: 1,
|
||
precision: 0,
|
||
placeholder: '数量',
|
||
value: isEmptyNum(v) ? null : toIntQty(v),
|
||
status: fieldInvalid(record, 'laborQty') ? 'error' : undefined,
|
||
onChange: function (val) { patch(record.key, 'laborQty', val == null ? null : toIntQty(val)); }
|
||
});
|
||
}
|
||
},
|
||
{
|
||
title: reqTitle('单价(含税)'),
|
||
dataIndex: 'laborUnitPrice',
|
||
key: 'laborUnitPrice',
|
||
width: 100,
|
||
align: 'right',
|
||
render: function (v, record) {
|
||
if (!canEditRow(record)) return readOnlyCell(fmtMoney(v), 'right');
|
||
return React.createElement(InputNumber, {
|
||
style: cellNumStyle,
|
||
min: 0,
|
||
precision: 2,
|
||
placeholder: '单价',
|
||
value: isEmptyNum(v) ? null : v,
|
||
status: fieldInvalid(record, 'laborUnitPrice') ? 'error' : undefined,
|
||
onChange: function (val) { patch(record.key, 'laborUnitPrice', val == null ? null : val); }
|
||
});
|
||
}
|
||
},
|
||
{
|
||
title: '金额(含税)',
|
||
dataIndex: 'laborAmount',
|
||
key: 'laborAmount',
|
||
width: 100,
|
||
align: 'right',
|
||
render: function (v) { return React.createElement('span', { style: { paddingRight: 8 } }, fmtMoney(v)); }
|
||
}
|
||
]
|
||
},
|
||
{
|
||
title: '配件费',
|
||
key: 'partsGroup',
|
||
children: [
|
||
{
|
||
title: reqTitle('单位'),
|
||
dataIndex: 'partsUnit',
|
||
key: 'partsUnit',
|
||
width: 72,
|
||
align: 'center',
|
||
render: function (v, record) {
|
||
if (!canEditRow(record)) return readOnlyCell(v);
|
||
return React.createElement(Input, {
|
||
className: 'maint-inline-input',
|
||
value: v,
|
||
placeholder: '单位',
|
||
status: fieldInvalid(record, 'partsUnit') ? 'error' : undefined,
|
||
onChange: function (e) { patch(record.key, 'partsUnit', e.target.value); }
|
||
});
|
||
}
|
||
},
|
||
{
|
||
title: reqTitle('数量'),
|
||
dataIndex: 'partsQty',
|
||
key: 'partsQty',
|
||
width: 88,
|
||
align: 'right',
|
||
render: function (v, record) {
|
||
if (!canEditRow(record)) return readOnlyCell(toIntQty(v), 'right');
|
||
return React.createElement(InputNumber, {
|
||
style: cellNumStyle,
|
||
min: 0,
|
||
step: 1,
|
||
precision: 0,
|
||
placeholder: '数量',
|
||
value: isEmptyNum(v) ? null : toIntQty(v),
|
||
status: fieldInvalid(record, 'partsQty') ? 'error' : undefined,
|
||
onChange: function (val) { patch(record.key, 'partsQty', val == null ? null : toIntQty(val)); }
|
||
});
|
||
}
|
||
},
|
||
{
|
||
title: reqTitle('单价(含税)'),
|
||
dataIndex: 'partsUnitPrice',
|
||
key: 'partsUnitPrice',
|
||
width: 100,
|
||
align: 'right',
|
||
render: function (v, record) {
|
||
if (!canEditRow(record)) return readOnlyCell(fmtMoney(v), 'right');
|
||
return React.createElement(InputNumber, {
|
||
style: cellNumStyle,
|
||
min: 0,
|
||
precision: 2,
|
||
placeholder: '单价',
|
||
value: isEmptyNum(v) ? null : v,
|
||
status: fieldInvalid(record, 'partsUnitPrice') ? 'error' : undefined,
|
||
onChange: function (val) { patch(record.key, 'partsUnitPrice', val == null ? null : val); }
|
||
});
|
||
}
|
||
},
|
||
{
|
||
title: '金额(含税)',
|
||
dataIndex: 'partsAmount',
|
||
key: 'partsAmount',
|
||
width: 100,
|
||
align: 'right',
|
||
render: function (v) { return React.createElement('span', { style: { paddingRight: 8 } }, fmtMoney(v)); }
|
||
}
|
||
]
|
||
},
|
||
{
|
||
title: '费用总计(含税)',
|
||
dataIndex: 'totalCost',
|
||
key: 'totalCost',
|
||
width: 120,
|
||
align: 'right',
|
||
fixed: 'right',
|
||
render: function (v) {
|
||
return React.createElement('span', { style: { paddingRight: 8, fontWeight: 600, color: '#0f172a' } }, fmtMoney(v));
|
||
}
|
||
},
|
||
{
|
||
title: '操作',
|
||
key: 'action',
|
||
width: 128,
|
||
align: 'center',
|
||
fixed: 'right',
|
||
render: function (_, record) {
|
||
var ref = latestRefs.current;
|
||
var submitted = record.submitStatus === 'submitted';
|
||
var isOwn = record.createdBy === CURRENT_USER.id;
|
||
var allowCopy = canCopyRow(record, ref.isSupervisor);
|
||
|
||
function renderEditIcon() {
|
||
return React.createElement(
|
||
'svg',
|
||
{ viewBox: '64 64 896 896', width: 14, height: 14, fill: 'currentColor', 'aria-hidden': true },
|
||
React.createElement('path', {
|
||
d: 'M257.7 752c2 0 4-.2 6-.5L431.9 722c2-.4 3.9-1.3 5.3-2.8l423.9-423.9a9.96 9.96 0 0 0 0-14.1L694.9 114.9c-1.9-1.9-4.4-2.9-7.1-2.9s-5.2 1-7.1 2.9L256.8 538.8c-1.5 1.5-2.4 3.3-2.8 5.3l-29.5 168.2a33.5 33.5 0 0 0 9.4 29.8c6.6 6.4 14.9 9.9 23.8 9.9z'
|
||
})
|
||
);
|
||
}
|
||
|
||
function renderCopyIcon() {
|
||
return React.createElement(
|
||
'svg',
|
||
{ viewBox: '64 64 896 896', width: 14, height: 14, fill: 'currentColor', 'aria-hidden': true },
|
||
React.createElement('path', {
|
||
d: 'M832 64H296c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h496v688c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8V96c0-17.7-14.3-32-32-32zM704 192H192c-17.7 0-32 14.3-32 32v530c0 17.7 14.3 32 32 32h512c17.7 0 32-14.3 32-32V224c0-17.7-14.3-32-32-32z'
|
||
})
|
||
);
|
||
}
|
||
|
||
function renderDeleteIcon() {
|
||
return React.createElement(
|
||
'svg',
|
||
{ viewBox: '64 64 896 896', width: 14, height: 14, fill: 'currentColor', 'aria-hidden': true },
|
||
React.createElement('path', {
|
||
d: 'M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60v520c0 17.7 14.3 32 32 32h632c17.7 0 32-14.3 32-32V336h60c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM512 464c-17.7 0-32 14.3-32 32v288c0 17.7 14.3 32 32 32h64c17.7 0 32-14.3 32-32V496c0-17.7-14.3-32-32-32h-64zm-192 0c-17.7 0-32 14.3-32 32v288c0 17.7 14.3 32 32 32h64c17.7 0 32-14.3 32-32V496c0-17.7-14.3-32-32-32h-64zm-192 0c-17.7 0-32 14.3-32 32v288c0 17.7 14.3 32 32 32h64c17.7 0 32-14.3 32-32V496c0-17.7-14.3-32-32-32h-64z'
|
||
})
|
||
);
|
||
}
|
||
|
||
function renderMoreIcon() {
|
||
return React.createElement(
|
||
'svg',
|
||
{ viewBox: '0 0 16 16', width: 16, height: 16, fill: 'currentColor', 'aria-hidden': true },
|
||
React.createElement('circle', { cx: '8', cy: '3', r: '1.5' }),
|
||
React.createElement('circle', { cx: '8', cy: '8', r: '1.5' }),
|
||
React.createElement('circle', { cx: '8', cy: '13', r: '1.5' })
|
||
);
|
||
}
|
||
|
||
function renderIconAction(title, iconNode, onClick, extraClass, disabled) {
|
||
var cls =
|
||
'maint-action-icon-btn' + (extraClass ? ' ' + extraClass : '') + (disabled ? ' is-disabled' : '');
|
||
var node = React.createElement(
|
||
'span',
|
||
{
|
||
className: cls,
|
||
role: 'button',
|
||
tabIndex: disabled ? -1 : 0,
|
||
'aria-label': title,
|
||
onClick:
|
||
disabled || !onClick
|
||
? undefined
|
||
: function (e) {
|
||
e.stopPropagation();
|
||
onClick(e);
|
||
}
|
||
},
|
||
iconNode
|
||
);
|
||
return React.createElement(Tooltip, { title: title }, node);
|
||
}
|
||
|
||
function renderMoreAction() {
|
||
return React.createElement(
|
||
Dropdown,
|
||
{
|
||
trigger: ['hover'],
|
||
placement: 'bottomRight',
|
||
menu: {
|
||
items: [
|
||
{
|
||
key: 'changelog',
|
||
label: '变更日志',
|
||
onClick: function () {
|
||
openChangeLogModal(record);
|
||
}
|
||
}
|
||
]
|
||
}
|
||
},
|
||
renderIconAction('更多', renderMoreIcon(), null, 'maint-row-more-btn')
|
||
);
|
||
}
|
||
|
||
function renderCopyButton() {
|
||
if (!allowCopy) return null;
|
||
var popOpen = copyPopoverKey === record.key;
|
||
var popContent = React.createElement(
|
||
'div',
|
||
{ style: { width: 220 } },
|
||
React.createElement(
|
||
'div',
|
||
{ style: { marginBottom: 8, fontSize: 13, color: 'rgba(15,23,42,0.65)' } },
|
||
'复制当前行已填写的全部数据'
|
||
),
|
||
React.createElement(
|
||
'div',
|
||
{ style: { marginBottom: 6, fontSize: 13, color: 'rgba(15,23,42,0.55)' } },
|
||
'复制行数'
|
||
),
|
||
React.createElement(InputNumber, {
|
||
style: { width: '100%', marginBottom: 10 },
|
||
min: 1,
|
||
max: 100,
|
||
precision: 0,
|
||
value: popOpen ? copyRowCount : 1,
|
||
onChange: function (val) {
|
||
setCopyRowCount(val == null ? 1 : val);
|
||
}
|
||
}),
|
||
React.createElement(
|
||
Button,
|
||
{
|
||
type: 'primary',
|
||
block: true,
|
||
size: 'small',
|
||
onClick: function () {
|
||
applyCopyRows(record.key, copyRowCount);
|
||
}
|
||
},
|
||
'确认复制'
|
||
)
|
||
);
|
||
return React.createElement(
|
||
Popover,
|
||
{
|
||
trigger: 'click',
|
||
placement: 'leftTop',
|
||
open: popOpen,
|
||
content: popContent,
|
||
onOpenChange: function (open) {
|
||
if (open) {
|
||
setCopyPopoverKey(record.key);
|
||
setCopyRowCount(1);
|
||
} else if (copyPopoverKey === record.key) {
|
||
setCopyPopoverKey(null);
|
||
}
|
||
}
|
||
},
|
||
renderIconAction('复制', renderCopyIcon())
|
||
);
|
||
}
|
||
|
||
function toggleEditKeys() {
|
||
ref.setEditingKeys(function (prev) {
|
||
if (prev.indexOf(record.key) >= 0) {
|
||
return prev.filter(function (k) { return k !== record.key; });
|
||
}
|
||
return prev.concat([record.key]);
|
||
});
|
||
}
|
||
|
||
function renderEditDeletePair(onEditClick, opts) {
|
||
opts = opts || {};
|
||
var showCopy = opts.showCopy !== false && allowCopy;
|
||
var allowEdit = opts.allowEdit !== false;
|
||
var allowDelete =
|
||
opts.allowDelete !== false && canDeleteRow(record, ref.isSupervisor);
|
||
var isEditing = ref.editingKeys.indexOf(record.key) >= 0;
|
||
var editTitle = opts.editDisabledTitle && !allowEdit
|
||
? opts.editDisabledTitle
|
||
: isEditing
|
||
? '完成编辑'
|
||
: '编辑';
|
||
var deleteTitle = opts.deleteDisabledTitle && !allowDelete
|
||
? opts.deleteDisabledTitle
|
||
: '删除';
|
||
var deleteBtn = React.createElement(
|
||
Popconfirm,
|
||
{
|
||
title: '确认删除该条维修/保养记录?',
|
||
disabled: !allowDelete,
|
||
onConfirm: function () {
|
||
if (!allowDelete) {
|
||
if (record.submitStatus === 'submitted') {
|
||
message.info('已提交记录仅运维主管可删除');
|
||
}
|
||
return;
|
||
}
|
||
ref.setAllRows(function (prev) {
|
||
return prev.filter(function (r) { return r.key !== record.key; });
|
||
});
|
||
ref.setEditingKeys(function (prev) {
|
||
return prev.filter(function (k) { return k !== record.key; });
|
||
});
|
||
message.success('已删除');
|
||
}
|
||
},
|
||
renderIconAction(
|
||
deleteTitle,
|
||
renderDeleteIcon(),
|
||
null,
|
||
'maint-action-icon-danger',
|
||
!allowDelete
|
||
)
|
||
);
|
||
var nodes = [
|
||
renderIconAction(
|
||
editTitle,
|
||
renderEditIcon(),
|
||
allowEdit ? onEditClick : null,
|
||
'',
|
||
!allowEdit
|
||
)
|
||
];
|
||
if (showCopy) nodes.push(renderCopyButton());
|
||
nodes.push(deleteBtn, renderMoreAction());
|
||
return React.createElement(
|
||
'div',
|
||
{ style: { display: 'inline-flex', alignItems: 'center', gap: 2, flexWrap: 'nowrap' } },
|
||
nodes
|
||
);
|
||
}
|
||
|
||
if (submitted) {
|
||
if (!canShowSubmittedActions(record, ref.isSupervisor)) {
|
||
return renderMoreAction();
|
||
}
|
||
return renderEditDeletePair(
|
||
function () {
|
||
if (!ref.isSupervisor) {
|
||
message.info('已提交记录仅运维主管可编辑');
|
||
return;
|
||
}
|
||
toggleEditKeys();
|
||
},
|
||
{ showCopy: false }
|
||
);
|
||
}
|
||
|
||
if (isSavedDraftRow(record)) {
|
||
if (!canShowSavedActions(record)) return renderMoreAction();
|
||
return renderEditDeletePair(function () {
|
||
if (!ref.isSupervisor && record.createdBy !== CURRENT_USER.id) {
|
||
message.info('仅可编辑本人提报的维修/保养记录');
|
||
return;
|
||
}
|
||
toggleEditKeys();
|
||
});
|
||
}
|
||
|
||
if (ref.isSupervisor || !isOwn) return renderMoreAction();
|
||
|
||
return React.createElement(
|
||
'div',
|
||
{ style: { display: 'inline-flex', alignItems: 'center', gap: 2, flexWrap: 'nowrap' } },
|
||
renderCopyButton(),
|
||
React.createElement(
|
||
Popconfirm,
|
||
{
|
||
title: '确认删除该条维修/保养记录?',
|
||
onConfirm: function () {
|
||
ref.setAllRows(function (prev) {
|
||
return prev.filter(function (r) { return r.key !== record.key; });
|
||
});
|
||
message.success('已删除');
|
||
}
|
||
},
|
||
renderIconAction('删除', renderDeleteIcon(), null, 'maint-action-icon-danger')
|
||
),
|
||
renderMoreAction()
|
||
);
|
||
}
|
||
}
|
||
];
|
||
}, [
|
||
canEditRow,
|
||
editingKeys,
|
||
isSupervisor,
|
||
rowInvalid,
|
||
copyPopoverKey,
|
||
copyRowCount,
|
||
applyCopyRows,
|
||
openChangeLogModal
|
||
]);
|
||
|
||
var changeLogRows = useMemo(
|
||
function () {
|
||
if (!changeLogModal.rowKey) return [];
|
||
return changeLogsByKey[changeLogModal.rowKey] || [];
|
||
},
|
||
[changeLogModal.rowKey, changeLogsByKey]
|
||
);
|
||
|
||
var changeLogColumns = useMemo(
|
||
function () {
|
||
return [
|
||
{
|
||
title: '修改时间',
|
||
dataIndex: 'at',
|
||
key: 'at',
|
||
width: 168,
|
||
render: function (v) {
|
||
return formatDateTime(v) || '—';
|
||
}
|
||
},
|
||
{
|
||
title: '修改人',
|
||
dataIndex: 'userName',
|
||
key: 'userName',
|
||
width: 88,
|
||
align: 'center'
|
||
},
|
||
{
|
||
title: '修改字段',
|
||
dataIndex: 'fieldLabel',
|
||
key: 'fieldLabel',
|
||
width: 140,
|
||
align: 'center'
|
||
},
|
||
{
|
||
title: '修改前',
|
||
dataIndex: 'before',
|
||
key: 'before',
|
||
ellipsis: true,
|
||
render: function (v) {
|
||
return React.createElement('span', { style: { color: '#cf1322' } }, v == null || v === '' ? '—' : v);
|
||
}
|
||
},
|
||
{
|
||
title: '修改后',
|
||
dataIndex: 'after',
|
||
key: 'after',
|
||
ellipsis: true,
|
||
render: function (v) {
|
||
return React.createElement('span', { style: { color: '#389e0d' } }, v == null || v === '' ? '—' : v);
|
||
}
|
||
}
|
||
];
|
||
},
|
||
[]
|
||
);
|
||
|
||
var tableSummary = useCallback(function () {
|
||
return React.createElement(
|
||
Table.Summary,
|
||
null,
|
||
React.createElement(
|
||
Table.Summary.Row,
|
||
null,
|
||
React.createElement(Table.Summary.Cell, { index: 0, align: 'center' }, '合计'),
|
||
React.createElement(Table.Summary.Cell, { index: 1, colSpan: 7 }),
|
||
React.createElement(Table.Summary.Cell, { index: 8, colSpan: 3 }),
|
||
React.createElement(Table.Summary.Cell, { index: 11, align: 'right' }, fmtMoney(totals.laborAmount)),
|
||
React.createElement(Table.Summary.Cell, { index: 12, colSpan: 3 }),
|
||
React.createElement(Table.Summary.Cell, { index: 15, align: 'right' }, fmtMoney(totals.partsAmount)),
|
||
React.createElement(Table.Summary.Cell, { index: 16, align: 'right' }, fmtMoney(totals.totalCost)),
|
||
React.createElement(Table.Summary.Cell, { index: 17 })
|
||
)
|
||
);
|
||
}, [totals]);
|
||
|
||
return React.createElement(
|
||
App,
|
||
null,
|
||
React.createElement('style', null, ledgerTableStyle),
|
||
React.createElement(
|
||
'div',
|
||
{ style: layoutStyle },
|
||
React.createElement(Breadcrumb, {
|
||
style: { marginBottom: 12 },
|
||
items: [{ title: '台账数据' }, { title: '车辆维修/保养明细' }]
|
||
}),
|
||
React.createElement(
|
||
Card,
|
||
{ style: filterCardStyle, bodyStyle: { paddingBottom: 4 } },
|
||
React.createElement(
|
||
Row,
|
||
{ gutter: [16, 16], align: 'bottom' },
|
||
React.createElement(
|
||
Col,
|
||
{ xs: 24, sm: 12, md: 8, lg: 5 },
|
||
React.createElement(
|
||
'div',
|
||
{ style: filterItemStyle },
|
||
React.createElement('div', { style: filterLabelStyle }, '进修日期'),
|
||
React.createElement(DatePicker.RangePicker, {
|
||
style: filterControlStyle,
|
||
format: 'YYYY-MM-DD',
|
||
value: dateRangeDraft,
|
||
onChange: function (v) { setDateRangeDraft(v); }
|
||
})
|
||
)
|
||
),
|
||
React.createElement(
|
||
Col,
|
||
{ xs: 24, sm: 12, md: 8, lg: 4 },
|
||
React.createElement(
|
||
'div',
|
||
{ style: filterItemStyle },
|
||
React.createElement('div', { style: filterLabelStyle }, '车牌号'),
|
||
React.createElement(Select, {
|
||
allowClear: true,
|
||
showSearch: true,
|
||
placeholder: '全部,可输入车牌搜索',
|
||
style: filterControlStyle,
|
||
value: plateDraft,
|
||
onChange: function (v) { setPlateDraft(v); },
|
||
options: vehiclePlateOptions,
|
||
optionFilterProp: 'label',
|
||
filterOption: filterPlateOption
|
||
})
|
||
)
|
||
),
|
||
React.createElement(
|
||
Col,
|
||
{ xs: 24, sm: 12, md: 8, lg: 4 },
|
||
React.createElement(
|
||
'div',
|
||
{ style: filterItemStyle },
|
||
React.createElement('div', { style: filterLabelStyle }, '类型'),
|
||
React.createElement(Select, {
|
||
mode: 'multiple',
|
||
allowClear: true,
|
||
placeholder: '全部',
|
||
style: filterControlStyle,
|
||
value: typeDraft,
|
||
onChange: function (v) { setTypeDraft(v || []); },
|
||
options: RECORD_TYPE_OPTIONS,
|
||
maxTagCount: 2
|
||
})
|
||
)
|
||
),
|
||
React.createElement(
|
||
Col,
|
||
{ xs: 24, sm: 12, md: 8, lg: 5 },
|
||
React.createElement(
|
||
'div',
|
||
{ style: filterItemStyle },
|
||
React.createElement('div', { style: filterLabelStyle }, '提报人'),
|
||
React.createElement(Select, {
|
||
mode: 'multiple',
|
||
allowClear: true,
|
||
showSearch: true,
|
||
placeholder: '全部',
|
||
style: filterControlStyle,
|
||
value: reporterDraft,
|
||
onChange: function (v) { setReporterDraft(v || []); },
|
||
options: reporterOptions,
|
||
filterOption: filterOption,
|
||
maxTagCount: 2
|
||
})
|
||
)
|
||
),
|
||
React.createElement(
|
||
Col,
|
||
{ xs: 24, sm: 12, md: 8, lg: 6, style: filterActionsColStyle },
|
||
React.createElement(
|
||
'div',
|
||
{ style: filterItemStyle },
|
||
React.createElement('div', { style: filterLabelStyle }, '\u00a0'),
|
||
React.createElement(
|
||
Space,
|
||
{ wrap: true },
|
||
React.createElement(Button, { onClick: handleReset }, '重置'),
|
||
React.createElement(Button, { type: 'primary', onClick: handleQuery }, '查询')
|
||
)
|
||
)
|
||
)
|
||
)
|
||
),
|
||
React.createElement(
|
||
Card,
|
||
{ style: tableCardStyle, bodyStyle: { padding: '20px 20px 24px' } },
|
||
React.createElement(
|
||
'div',
|
||
{ style: { position: 'relative', marginBottom: 8, minHeight: 36 } },
|
||
React.createElement(
|
||
'div',
|
||
{
|
||
style: {
|
||
textAlign: 'center',
|
||
fontSize: 18,
|
||
fontWeight: 700,
|
||
color: 'rgba(15,23,42,0.92)',
|
||
letterSpacing: '0.02em',
|
||
padding: '0 200px'
|
||
}
|
||
},
|
||
'车辆维修/保养明细'
|
||
),
|
||
React.createElement(
|
||
'div',
|
||
{ style: { position: 'absolute', right: 0, top: '50%', transform: 'translateY(-50%)' } },
|
||
React.createElement(
|
||
Space,
|
||
null,
|
||
React.createElement(Button, { onClick: handleSave }, '保存'),
|
||
!isSupervisor
|
||
? React.createElement(Button, { type: 'primary', onClick: handleConfirmSubmit }, '确认提交')
|
||
: null
|
||
)
|
||
)
|
||
),
|
||
React.createElement(
|
||
'div',
|
||
{ className: 'maint-ledger-table-wrap' },
|
||
React.createElement(Table, {
|
||
className: 'maint-ledger-table',
|
||
size: 'small',
|
||
bordered: true,
|
||
rowKey: 'key',
|
||
columns: columns,
|
||
dataSource: visibleRows,
|
||
pagination: false,
|
||
rowClassName: function (record, index) {
|
||
var tier = record.displayTier != null ? record.displayTier : rowDisplaySortTier(record);
|
||
var prev = visibleRows[index - 1];
|
||
var prevTier = prev ? (prev.displayTier != null ? prev.displayTier : rowDisplaySortTier(prev)) : tier;
|
||
var cls = 'maint-row-data maint-row-tier-' + tier;
|
||
if (index > 0 && prevTier !== tier) cls += ' maint-row-tier-boundary';
|
||
return cls;
|
||
},
|
||
scroll: { x: 'max-content', y: 'calc(100vh - 360px)' },
|
||
sticky: true,
|
||
summary: tableSummary
|
||
})
|
||
),
|
||
!isSupervisor
|
||
? React.createElement(
|
||
Button,
|
||
{
|
||
type: 'dashed',
|
||
block: true,
|
||
style: { marginTop: 12 },
|
||
onClick: addRow
|
||
},
|
||
'新增一行'
|
||
)
|
||
: null
|
||
),
|
||
React.createElement(
|
||
Modal,
|
||
{
|
||
title: '变更日志' + (changeLogModal.rowLabel ? ' · ' + changeLogModal.rowLabel : ''),
|
||
open: changeLogModal.open,
|
||
onCancel: closeChangeLogModal,
|
||
footer: React.createElement(Button, { onClick: closeChangeLogModal }, '关闭'),
|
||
width: 880,
|
||
destroyOnClose: true
|
||
},
|
||
React.createElement(Table, {
|
||
size: 'small',
|
||
bordered: true,
|
||
rowKey: 'id',
|
||
pagination: changeLogRows.length > 8 ? { pageSize: 8, showSizeChanger: false } : false,
|
||
columns: changeLogColumns,
|
||
dataSource: changeLogRows,
|
||
locale: { emptyText: '暂无变更记录' },
|
||
scroll: { x: 'max-content', y: 360 }
|
||
})
|
||
)
|
||
)
|
||
);
|
||
};
|