const FORM_SELECTOR = '.el-dialog .el-form'; const ITEM_SELECTOR = '.el-form-item'; const COLUMN_TOLERANCE = 8; const REQUIRED_MARK_WIDTH = 12; let textMeasureEl; const isVisible = element => { if (!element) return false; const rect = element.getBoundingClientRect(); return rect.width > 0 && rect.height > 0; }; const getDirectLabel = item => { return Array.from(item.children).find(child => child.classList.contains('el-form-item__label')); }; const getColumnLeft = item => { const column = item.closest('.el-col'); const target = column || item; return Math.round(target.getBoundingClientRect().left); }; const findColumnGroup = (groups, left) => { return groups.find(group => Math.abs(group.left - left) <= COLUMN_TOLERANCE); }; const getTextMeasureEl = () => { if (textMeasureEl) return textMeasureEl; textMeasureEl = document.createElement('span'); textMeasureEl.style.position = 'fixed'; textMeasureEl.style.left = '-9999px'; textMeasureEl.style.top = '-9999px'; textMeasureEl.style.visibility = 'hidden'; textMeasureEl.style.whiteSpace = 'nowrap'; textMeasureEl.style.pointerEvents = 'none'; document.body.appendChild(textMeasureEl); return textMeasureEl; }; const measureLabelWidth = (label, item) => { const text = String(label.textContent || '').trim(); if (!text) return 0; const style = window.getComputedStyle(label); const measurer = getTextMeasureEl(); measurer.style.font = style.font; measurer.style.letterSpacing = style.letterSpacing; measurer.textContent = text; const paddingLeft = Number.parseFloat(style.paddingLeft) || 0; const paddingRight = Number.parseFloat(style.paddingRight) || 0; const requiredWidth = item.classList.contains('is-required') ? REQUIRED_MARK_WIDTH : 0; return Math.ceil(measurer.getBoundingClientRect().width + paddingLeft + paddingRight + requiredWidth); }; const collectLabelGroups = form => { const groups = []; const items = Array.from(form.querySelectorAll(ITEM_SELECTOR)); items.forEach(item => { if (!isVisible(item) || item.closest('.avue-form__menu')) return; const label = getDirectLabel(item); if (!label || !isVisible(label)) return; const width = measureLabelWidth(label, item); if (!width) return; const left = getColumnLeft(item); let group = findColumnGroup(groups, left); if (!group) { group = { left, labels: [], width: 0, }; groups.push(group); } group.labels.push(label); group.width = Math.max(group.width, width); }); return groups; }; const alignFormLabelsByColumn = form => { if (form.classList.contains('dialog-form-label-fixed')) return; const groups = collectLabelGroups(form); groups.forEach(group => { group.labels.forEach(label => { const width = `${group.width}px`; if (label.style.width !== width) { label.style.width = width; } if (label.style.flexBasis !== width) { label.style.flexBasis = width; } if (label.style.justifyContent !== 'flex-end') { label.style.justifyContent = 'flex-end'; } if (label.style.textAlign !== 'right') { label.style.textAlign = 'right'; } }); }); }; const alignAllDialogFormLabels = () => { document.querySelectorAll(FORM_SELECTOR).forEach(alignFormLabelsByColumn); }; export const setupDialogFormLabelAlign = () => { if (typeof window === 'undefined' || typeof document === 'undefined') return; let frameId = 0; const scheduleAlign = () => { if (frameId) return; frameId = window.requestAnimationFrame(() => { frameId = 0; alignAllDialogFormLabels(); }); }; scheduleAlign(); window.addEventListener('resize', scheduleAlign); const observer = new MutationObserver(scheduleAlign); observer.observe(document.body, { attributes: true, attributeFilter: ['class', 'style'], childList: true, subtree: true, }); };