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.verticalAlign = inputStyles.verticalAlign;
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
@ -154,14 +168,28 @@ const VariableInput: React.FC<VariableInputProps> = ({
highlightElement.scrollLeft = inputElement.scrollLeft;
};
// 初始同步(延迟执行,确保 DOM 完全渲染)
// 初始同步(多次延迟确保 DOM 完全渲染,包括 Modal/Sheet 的样式应用
syncStyles();
setTimeout(syncStyles, 0);
setTimeout(syncStyles, 10);
setTimeout(syncStyles, 50);
setTimeout(syncStyles, 100);
// 监听窗口大小变化(可能影响字体大小)
const resizeObserver = new ResizeObserver(syncStyles);
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
if (variant === 'textarea') {
inputElement.addEventListener('scroll', syncScroll);
@ -169,6 +197,7 @@ const VariableInput: React.FC<VariableInputProps> = ({
return () => {
resizeObserver.disconnect();
mutationObserver.disconnect();
if (variant === 'textarea') {
inputElement.removeEventListener('scroll', syncScroll);
}
@ -178,6 +207,40 @@ const VariableInput: React.FC<VariableInputProps> = ({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [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(() => {
if (!showSuggestions) return;
@ -452,9 +515,14 @@ const VariableInput: React.FC<VariableInputProps> = ({
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} />
</div>
) : (
<HighlightLayer value={value} />
</div>
)}
</div>
)}
</div>