This commit is contained in:
dengqichen 2025-10-22 18:37:33 +08:00
parent 5851c98537
commit 3140456ee0

View File

@ -144,6 +144,20 @@ const VariableInput: React.FC<VariableInputProps> = ({
highlightElement.style.overflowWrap = inputStyles.overflowWrap; highlightElement.style.overflowWrap = inputStyles.overflowWrap;
highlightElement.style.verticalAlign = inputStyles.verticalAlign; highlightElement.style.verticalAlign = inputStyles.verticalAlign;
highlightElement.style.boxSizing = inputStyles.boxSizing; highlightElement.style.boxSizing = inputStyles.boxSizing;
// 关键:同步文本对齐属性,确保光标位置准确
highlightElement.style.textAlign = inputStyles.textAlign;
highlightElement.style.direction = inputStyles.direction;
highlightElement.style.textIndent = inputStyles.textIndent;
// 调试信息(仅开发环境)
if (process.env.NODE_ENV === 'development' && variant === 'textarea') {
console.log('[VariableInput] 样式同步:', {
fontSize: inputStyles.fontSize,
lineHeight: inputStyles.lineHeight,
padding: `${inputStyles.paddingTop} ${inputStyles.paddingRight} ${inputStyles.paddingBottom} ${inputStyles.paddingLeft}`,
});
}
}; };
// 同步滚动位置(仅 textarea // 同步滚动位置(仅 textarea
@ -154,14 +168,28 @@ const VariableInput: React.FC<VariableInputProps> = ({
highlightElement.scrollLeft = inputElement.scrollLeft; highlightElement.scrollLeft = inputElement.scrollLeft;
}; };
// 初始同步(延迟执行,确保 DOM 完全渲染) // 初始同步(多次延迟确保 DOM 完全渲染,包括 Modal/Sheet 的样式应用
syncStyles(); syncStyles();
setTimeout(syncStyles, 0); setTimeout(syncStyles, 0);
setTimeout(syncStyles, 10);
setTimeout(syncStyles, 50);
setTimeout(syncStyles, 100);
// 监听窗口大小变化(可能影响字体大小) // 监听窗口大小变化(可能影响字体大小)
const resizeObserver = new ResizeObserver(syncStyles); const resizeObserver = new ResizeObserver(syncStyles);
resizeObserver.observe(inputElement); resizeObserver.observe(inputElement);
// 监听父元素属性变化(例如 Modal/Sheet 的显示状态)
const mutationObserver = new MutationObserver(syncStyles);
let parent = inputElement.parentElement;
while (parent && parent !== document.body) {
mutationObserver.observe(parent, {
attributes: true,
attributeFilter: ['class', 'style'],
});
parent = parent.parentElement;
}
// 监听滚动事件(仅 textarea // 监听滚动事件(仅 textarea
if (variant === 'textarea') { if (variant === 'textarea') {
inputElement.addEventListener('scroll', syncScroll); inputElement.addEventListener('scroll', syncScroll);
@ -169,6 +197,7 @@ const VariableInput: React.FC<VariableInputProps> = ({
return () => { return () => {
resizeObserver.disconnect(); resizeObserver.disconnect();
mutationObserver.disconnect();
if (variant === 'textarea') { if (variant === 'textarea') {
inputElement.removeEventListener('scroll', syncScroll); inputElement.removeEventListener('scroll', syncScroll);
} }
@ -178,6 +207,40 @@ const VariableInput: React.FC<VariableInputProps> = ({
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [variant]); }, [variant]);
// 当 value 变化时,确保样式同步(特别是首次输入时)
useEffect(() => {
if (!inputRef.current || !highlightRef.current || !value) return;
const syncCriticalStyles = () => {
const inputElement = inputRef.current;
const highlightContainer = highlightRef.current;
if (!inputElement || !highlightContainer) return;
const inputStyles = window.getComputedStyle(inputElement);
// 同步关键样式
highlightContainer.style.paddingTop = inputStyles.paddingTop;
highlightContainer.style.paddingBottom = inputStyles.paddingBottom;
highlightContainer.style.paddingLeft = inputStyles.paddingLeft;
highlightContainer.style.paddingRight = inputStyles.paddingRight;
highlightContainer.style.lineHeight = inputStyles.lineHeight;
highlightContainer.style.fontSize = inputStyles.fontSize;
highlightContainer.style.fontFamily = inputStyles.fontFamily;
};
// 多次延迟确保样式完全应用
syncCriticalStyles();
const timer1 = setTimeout(syncCriticalStyles, 0);
const timer2 = setTimeout(syncCriticalStyles, 10);
const timer3 = setTimeout(syncCriticalStyles, 50);
return () => {
clearTimeout(timer1);
clearTimeout(timer2);
clearTimeout(timer3);
};
}, [value]);
// 监听滚动和窗口大小变化,更新下拉框位置 // 监听滚动和窗口大小变化,更新下拉框位置
useEffect(() => { useEffect(() => {
if (!showSuggestions) return; if (!showSuggestions) return;
@ -452,9 +515,14 @@ const VariableInput: React.FC<VariableInputProps> = ({
alignItems: variant === 'input' ? 'center' : 'flex-start', alignItems: variant === 'input' ? 'center' : 'flex-start',
}} }}
> >
<div style={{ width: '100%', flex: variant === 'input' ? 'none' : undefined }}> {/* Input 需要包装 div 避免 flex 影响文字Textarea 直接渲染 */}
{variant === 'input' ? (
<div style={{ width: '100%' }}>
<HighlightLayer value={value} /> <HighlightLayer value={value} />
</div> </div>
) : (
<HighlightLayer value={value} />
)}
</div> </div>
)} )}
</div> </div>