当前位置: 首页 > news >正文

实现类似word 文档下划线输入功能

效果图:

组件:

<template><span class="underline-input-wrapper" @click="focusEditable"><spanref="editable"class="underline-editable"contenteditable="true":style="{minWidth: minWidth,'--min-chars': 3,}"@input="handleInput"@blur="handleBlur"@keydown.enter.prevent="insertLineBreak"@compositionstart="isComposing = true"@compositionend="handleCompositionEnd"></span></span>
</template><script setup lang="ts">
import { ref, onMounted, watch, nextTick, toRefs } from "vue";const props = defineProps({modelValue: {type: String,default: () => "          ",},minWidth: {type: String,default: "20px",},dashed: {type: Boolean,default: false,},lineType: {type: String,default: () => "dashed",},
});
const { lineType } = toRefs(props);const emit = defineEmits(["blur", "update:value"]);const editable = ref<any>(null);
const isComposing = ref(false);onMounted(() => {if (props.modelValue) {renderContent(props.modelValue);}
});watch(() => props.modelValue,(newVal) => {if (newVal !== getDisplayText() && !isComposing.value) {renderContent(newVal);}},
);const renderContent = async (text: string) => {if (!editable.value) return;const cursorPos = getCursorPosition();editable.value.innerHTML = text.split("").map((char) =>char === "\n"? `<span class="underline-char ${lineType.value}-line"><br></span>`: `<span class="underline-char ${lineType.value}-line">${char}</span>`,).join("");await nextTick();setCursorPosition(cursorPos);
};const getDisplayText = () => {return editable.value?.textContent || "";
};const handleInput = () => {if (isComposing.value) return;const text = getDisplayText();emit("update:value", text.trim());if (text !== props.modelValue) {renderContent(text);}
};const handleCompositionEnd = () => {isComposing.value = false;handleInput();
};const insertLineBreak = () => {const selection = window.getSelection();if (!selection || selection.rangeCount === 0) return;// 获取当前选区范围const range = selection.getRangeAt(0);range.deleteContents(); // 清除当前选中的内容// 创建要插入的元素const span = document.createElement("span");span.className = `underline-char ${lineType.value}-line`;span.innerHTML = "<br>";// 插入元素到文档中range.insertNode(span);// 移动光标到插入的元素后面range.setStartAfter(span);range.setEndAfter(span);// 更新选区selection.removeAllRanges();selection.addRange(range);handleInput();
};const getCursorPosition = () => {const selection: any = window.getSelection();if (selection.rangeCount === 0) return 0;const range = selection.getRangeAt(0);let pos = 0;const walker = document.createTreeWalker(editable.value, NodeFilter.SHOW_TEXT, null);let node: any;while ((node = walker.nextNode())) {if (node === range.startContainer) {pos += range.startOffset;break;}pos += node.length;}return pos;
};const setCursorPosition = (pos: any) => {const selection: any = window.getSelection();const range = document.createRange();let count = 0;const walker = document.createTreeWalker(editable.value, NodeFilter.SHOW_TEXT, null);let node: any;while ((node = walker.nextNode())) {if (count + node.length >= pos) {range.setStart(node, pos - count);range.collapse(true);selection.removeAllRanges();selection.addRange(range);break;}count += node.length;}
};const focusEditable = () => {editable.value.focus();
};const handleBlur = () => {emit("blur", getDisplayText());
};
</script><style lang="less">
.underline-input-wrapper {cursor: text;line-height: 1.5;
}.underline-editable {display: inline;min-width: v-bind("minWidth");outline: none;white-space: nowrap;
}.underline-char {display: inline;padding-bottom: 0px;white-space: pre-wrap;position: relative;
}
.dashed-line {margin-right: 2px;border-bottom: 1px dashed #000;
}
.solid-line {border-bottom: 1px solid #000;
}.underline-char:empty::after,
.underline-char br::after {content: "\200B";display: inline;
}/* 处理换行情况 */
.underline-char br {display: block;content: "";margin-top: 1em;
}
</style>

引用案例:一个是默认展示内容,一个是绑定的值

 <dsahedInputComp:modelValue="` ${mctCertApplysValue?.officeUser} `"v-model:value="saveData.officeUser"/>

http://www.dtcms.com/a/391133.html

相关文章:

  • AR技术赋能高风险作业:重塑安全与效率
  • Axure-图片旋转人机验证
  • web:ts中class、interface、type的区别
  • css中的vm和vh,页面滚动的卡片网页
  • git cherry pick怎么用
  • 40岁从智驾转具身智能之路
  • 串口连接失败排坑步骤
  • QML学习笔记(三)QML基础语法其一:Rectangle及帮助文档的使用
  • 工业相机与镜头靶面尺寸的关系:从原理到选型的避坑指南
  • javaweb tomcat的使用
  • AI 赋能 APP 界面设计公司:从美学到交互的智能升级
  • Java 中的 static:从动机到内存模型、并发与最佳实践
  • 【数据结构与算法(C语言版)】从 0 实现 线性表之顺序表(代码实现增删查功能全解析)
  • MethodSignature signature = (MethodSignature) joinPoint.getSignature()
  • CN2香港服务器是否适合SEO优化?
  • 查看电脑IP地址、修改IP操作,查询本地网络使用的公网IP,判断路由器是否有公网IP,内网IP又怎么让外网上访问
  • 合成孔径雷达干涉测量InSAR:从星载与地基系统原理到多领域应用实战,涵盖数据处理、地形三维重建、形变监测与案例解析
  • Activity 之间跳转时,生命周期的变化
  • SortableItem拖拽组件里的Popconfirm失效
  • [吾爱原创] 图片尺寸调整-支持批量、多格式、缩小、放大、保留元数据、无损质量、最小体积、预览
  • 【C语言】C 语言文件操作全解析:从基础到进阶
  • 《工作流落地篇:工作流中涉及到的主要数据库表》
  • 实验二理解 Java 语言的基本结构和程序设计方法
  • 【开题答辩全过程】以 基于Java的社区医疗预约系统的设计与实现为例,包含答辩的问题和答案
  • 以虚筑实,虚拟仿真技术浇筑水利工程人才培养的数字基座
  • 拟声 0.79.1 | 高颜值,拟态风格,B站歌曲,可下载,可搜索歌词
  • 团体程序设计天梯赛-练习集 L1-038 新世界
  • 【MySQL】约束类型
  • AXI4 DDR读写测试
  • 一个.h .hpp 笔记