需求:根据数据中的html,因为我是在做填空,所以是需要将html中的_____替换成input,由于具体需求我使用的是元素contenteditable代替的可编辑的input
html部分
<div class="wrap"><component :is="renderedContent" ref="wrap_component" /></div>
js部分
// 这个是为了保证输入的时候光标保持在最后
const moveCursorToEnd = (element: HTMLElement) => {const range = document.createRange();const selection = window.getSelection();// 找到最后一个文本节点let lastTextNode: Text | any = null;const traverseNodes = (node: Node) => {if (node.nodeType === Node.TEXT_NODE) {lastTextNode = node as Text;}for (let i = 0; i < node.childNodes.length; i++) {traverseNodes(node.childNodes[i]);}};traverseNodes(element);if (lastTextNode) {range.setStart(lastTextNode, lastTextNode.textContent?.length || 0);range.collapse(true);if (selection) {selection.removeAllRanges();selection.addRange(range);}} else {range.setStart(element, 0);range.collapse(true);if (selection) {selection.removeAllRanges();selection.addRange(range);}}// 兼容性处理:确保元素获取焦点element.focus();if (document.activeElement !== element) {element.focus();}
};// 计算属性,用于生成渲染内容
const renderedContent = computed(() => {if (!itemConf.value.customConf?.inputHtml) return null;const parts = itemConf.value.customConf.inputHtml.split(/_{1,}/);let nodes: any = [];parts.forEach((part, index) => {if (part) {const replacedSpaces = part.replace(/ /g, ' ');const replacedPart = replacedSpaces.replace(/<div>/g, '<br>').replace(/<\/div>/g, '');nodes.push(h('span', { class: 'custom-span', innerHTML: replacedPart }));}if (index < parts.length - 1) {if (!inputValues.value[index]) {inputValues.value[index] = '';}if (!isInputFocused.value[index]) {isInputFocused.value[index] = false;}if (!isClearIconClicked.value[index]) {isClearIconClicked.value[index] = false;}if (!clearIconHideTimer.value[index]) {clearIconHideTimer.value[index] = 0;}const clearIcon = h(ElIcon,{class: ['clear_icon',{'is-hidden':inputValues.value[index].length === 0 ||itemConf.value.baseConf.isReadOnly ||!isInputFocused.value[index],},],onClick: () => {if (!itemConf.value.baseConf.isReadOnly) {isClearIconClicked.value[index] = true;inputValues.value[index] = '';if (inputRefs.value[index]) {inputRefs.value[index].innerText = '';}adjustInputWidth(index);handleChange(itemConf.value.customConf.inputGroup[index], '');// 点击后清除隐藏定时器clearTimeout(clearIconHideTimer.value[index]);}},},{ default: () => h(CircleClose) },);const inputNode = h('p',{contenteditable: !itemConf.value.baseConf.isReadOnly,class: ['underline_input',{'is-disabled': itemConf.value.baseConf.isReadOnly,},],disabled: itemConf.value.baseConf.isReadOnly,innerHTML: inputValues.value[index],placeholder: unref(itemConf).customConf?.inputGroup[index]?.placeholder || '请输入',onInput: async (event: InputEvent) => {const target = event.target as HTMLParagraphElement;adjustInputWidth(index);inputValues.value[index] = target.innerHTML;await nextTick(() => {moveCursorToEnd(target);});},onFocus: () => {if (!itemConf.value.baseConf.isReadOnly) {isInputFocused.value[index] = true;clearTimeout(clearIconHideTimer.value[index]);}},onBlur: () => {if (!itemConf.value.baseConf.isReadOnly) {handleChange(itemConf.value.customConf.inputGroup[index], inputValues.value[index]);clearIconHideTimer.value[index] = setTimeout(() => {if (!isClearIconClicked.value[index]) {isInputFocused.value[index] = false;}isClearIconClicked.value[index] = false;}, 200);}},onMousedown: (event: MouseEvent) => {if (itemConf.value.baseConf.isReadOnly) {event.preventDefault();event.stopPropagation();}},onKeydown: (event: KeyboardEvent) => {if (itemConf.value.baseConf.isReadOnly) {event.preventDefault();event.stopPropagation();}},ref: (el) => (inputRefs.value[index] = el),},// [clearIcon],);nodes.push(h('p', { class: 'underline_input_wrap' }, [inputNode, clearIcon]));}});return h('div', nodes);
});
css部分
.underline_input_wrap {display: inline-block;// max-width: calc(100% - 70px);position: relative;margin-top: 20px;margin-bottom: 0;max-width: calc(100% - 50px);
}
.underline_input {position: relative;height: 40px;min-width: 101px;// max-width: calc(100% - 70px);max-width: 100%;background: #f5f7fb;border-radius: 6px 6px 6px 6px;border: none;margin-left: 10px;margin-top: 0;margin-bottom: 0;display: inline-block;box-sizing: border-box;padding: 0 26px 0 12px;background: #f5f7fb;vertical-align: middle;color: #606266;background: #f5f7fb;vertical-align: middle;&:focus {outline: none;border: 1px solid #1a77ff;color: #606266;}&:disabled {color: #bbbfc4;cursor: not-allowed;}&::placeholder {color: #a8abb2;font-size: 14px;}
}.underline_input.is-disabled {color: #bbbfc4;cursor: not-allowed;
}.underline_input[contenteditable='true']:empty::before,
.underline_input.is-disabled:empty::before {content: attr(placeholder);color: #bbbfc4;
}
:deep(.clear_icon) {position: absolute;width: 14px;height: 14px;right: 5px;top: 50%;transform: translateY(-50%);cursor: pointer;color: #999;z-index: 10; /* 增加 z-index 确保在最上层 */&:hover {color: #666;}&.is-hidden {display: none;}
}
我们要模拟input可清除,所以需要我们去调整样式,以及placeholder样式问题