mirror of https://github.com/veypi/OneAuth.git
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
266 lines
7.3 KiB
HTML
266 lines
7.3 KiB
HTML
<!doctype html>
|
|
<html>
|
|
|
|
<head>
|
|
<title>可编辑 Div 示例</title>
|
|
</head>
|
|
<style>
|
|
.editable-div {
|
|
border: 1px solid #ccc;
|
|
padding: 10px;
|
|
min-height: 100px;
|
|
outline: none;
|
|
white-space: pre-wrap;
|
|
}
|
|
|
|
.suggestions {
|
|
border: 1px solid #ddd;
|
|
background: white;
|
|
max-height: 150px;
|
|
overflow-y: auto;
|
|
position: absolute;
|
|
z-index: 1000;
|
|
}
|
|
|
|
.suggestion-item {
|
|
padding: 8px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.suggestion-item:hover {
|
|
background-color: #f0f0f0;
|
|
}
|
|
|
|
.selected-tag {
|
|
color: green;
|
|
font-weight: bold;
|
|
}
|
|
</style>
|
|
<style unscoped>
|
|
.test {
|
|
position: relative;
|
|
color: red;
|
|
display: inline-block;
|
|
min-width: 10px;
|
|
margin-left: 50px;
|
|
margin-right: 50px;
|
|
}
|
|
|
|
/* .test::before { */
|
|
/* position: absolute; */
|
|
/* left: -20px; */
|
|
/* content: '@'; */
|
|
/* display: inline-block; */
|
|
/* background: #0f0; */
|
|
/* width: 10px; */
|
|
/* } */
|
|
|
|
/* .test::after { */
|
|
/* content: '@'; */
|
|
/* width: 10px; */
|
|
/* display: inline-block; */
|
|
/* background: #0ff; */
|
|
/* /* content: attr(data-content) */
|
|
*/
|
|
/* } */
|
|
</style>
|
|
|
|
<body>
|
|
<div class="editable-div" contenteditable="true" @input="handleInput" @keydown="handleKeydown" ref="editableDiv">
|
|
</div>
|
|
|
|
<div class="suggestions" v-if="showSuggestions">
|
|
<div class="suggestion-item" v-for="(item, index) in filteredSuggestions" :key="index"
|
|
@mousedown="selectSuggestion(item)">
|
|
{{ item }}
|
|
</div>
|
|
</div>
|
|
</body>
|
|
<script setup>
|
|
// 初始化数据
|
|
textContent = "";
|
|
suggestions = ["Alice", "Bob", "Charlie", "David", "Eve"];
|
|
filteredSuggestions = [];
|
|
showSuggestions = false;
|
|
currentSelectionIndex = -1;
|
|
|
|
// 处理输入事件
|
|
handleInput = (e) => {
|
|
// const div = $node.querySelector(".editable-div");
|
|
// textContent = div.innerText;
|
|
|
|
|
|
|
|
// // 检测是否输入了 @ 符号
|
|
// const caretPosition = getCaretPosition(div);
|
|
// const textBeforeCaret = textContent.substring(0, caretPosition);
|
|
// const lastAtPos = textBeforeCaret.lastIndexOf("@");
|
|
//
|
|
// if (lastAtPos !== -1) {
|
|
// const query = textBeforeCaret.substring(lastAtPos + 1);
|
|
// filteredSuggestions = suggestions.filter((item) =>
|
|
// item.toLowerCase().includes(query.toLowerCase())
|
|
// );
|
|
// } else {
|
|
// showSuggestions = false;
|
|
// }
|
|
};
|
|
|
|
// 处理键盘事件(上下箭头、回车)
|
|
handleKeydown = (e) => {
|
|
const selection = window.getSelection();
|
|
|
|
const range = selection.getRangeAt(0);
|
|
let dom = selection.anchorNode;
|
|
if (dom.nodeType === 3) {
|
|
dom = dom.parentNode;
|
|
}
|
|
if (dom && dom.hasAttribute && dom.hasAttribute('test')) {
|
|
showSuggestions = true
|
|
let sub = dom.innerText.slice(1)
|
|
console.log(sub)
|
|
filteredSuggestions = suggestions.filter((item) =>
|
|
item.toLowerCase().includes(sub)
|
|
);
|
|
} else {
|
|
showSuggestions = false
|
|
}
|
|
if (!showSuggestions) {
|
|
if (e.key === '@') {
|
|
e.preventDefault();
|
|
const div = document.createElement('div');
|
|
div.setAttribute('test', '')
|
|
div.innerText = '@'
|
|
div.classList.add('test')
|
|
|
|
// 插入换行符到当前光标位置
|
|
range.deleteContents(); // 清除选中的内容(如果有)
|
|
// range.insertNode(document.createElement('div'))
|
|
range.insertNode(div);
|
|
|
|
// 确保光标移动到换行后的位置
|
|
// const nextNode = br.nextSibling || br.parentNode.appendChild(document.createTextNode(''));
|
|
const newRange = document.createRange();
|
|
newRange.setStartAfter(div.childNodes[0]);
|
|
newRange.collapse(true);
|
|
|
|
// 更新选区
|
|
selection.removeAllRanges();
|
|
selection.addRange(newRange);
|
|
}
|
|
return
|
|
}
|
|
|
|
if (e.key === "ArrowDown") {
|
|
e.preventDefault();
|
|
currentSelectionIndex =
|
|
(currentSelectionIndex + 1) % filteredSuggestions.length;
|
|
if (isNaN(currentSelectionIndex)) {
|
|
currentSelectionIndex = 0
|
|
}
|
|
highlightSuggestion();
|
|
} else if (e.key === "ArrowUp") {
|
|
e.preventDefault();
|
|
currentSelectionIndex =
|
|
(currentSelectionIndex - 1 + filteredSuggestions.length) %
|
|
filteredSuggestions.length;
|
|
if (isNaN(currentSelectionIndex)) {
|
|
currentSelectionIndex = 0
|
|
}
|
|
highlightSuggestion();
|
|
} else if (e.key === "Enter") {
|
|
e.preventDefault();
|
|
if (currentSelectionIndex !== -1) {
|
|
let item = filteredSuggestions[currentSelectionIndex]
|
|
console.log(dom, item)
|
|
dom.innerText = '@' + item
|
|
const newRange = document.createRange();
|
|
if (!dom.nextSibling) {
|
|
dom.parentNode.appendChild(document.createTextNode(''))
|
|
}
|
|
if (dom.nextSibling.nodeType === 3) {
|
|
let span = document.createElement('span')
|
|
span.innerText = dom.nextSibling.textContent || ' '
|
|
span.style.minWidth = '10px'
|
|
dom.nextSibling.replaceWith(span)
|
|
}
|
|
let next = dom.nextSibling
|
|
if (next.nodeType === 3) {
|
|
next = next.nextSibling
|
|
}
|
|
console.log([next])
|
|
newRange.setStartAfter(next);
|
|
newRange.collapse(true);
|
|
selection.removeAllRanges(); // 清除当前的选择
|
|
selection.addRange(newRange);
|
|
dom.setAttribute('contenteditable', 'false')
|
|
dom.style.contenteditable = false
|
|
// selectSuggestion(filteredSuggestions[currentSelectionIndex]);
|
|
}
|
|
}
|
|
};
|
|
|
|
// 选中建议项
|
|
selectSuggestion = (item) => {
|
|
const div = $node.querySelector(".editable-div");
|
|
const caretPosition = getCaretPosition(div);
|
|
const textBeforeCaret = textContent.substring(0, caretPosition);
|
|
const lastAtPos = textBeforeCaret.lastIndexOf("@");
|
|
|
|
// 替换 @ 后的内容为绿色标记
|
|
let before = textBeforeCaret.substring(0, lastAtPos);
|
|
let after = textContent.substring(caretPosition)
|
|
const newText = before +
|
|
`<span class="selected-tag">@${item}</span>` +
|
|
after
|
|
|
|
|
|
console.log(newText)
|
|
div.innerHTML = newText;
|
|
setCaretPosition(div, lastAtPos + 2); // 重新设置光标位置
|
|
|
|
// 隐藏建议框
|
|
showSuggestions = false;
|
|
currentSelectionIndex = -1;
|
|
|
|
// 更新文本内容
|
|
textContent = div.innerText;
|
|
};
|
|
|
|
// 获取光标位置
|
|
getCaretPosition = (element) => {
|
|
let position = 0;
|
|
const selection = window.getSelection();
|
|
if (selection.rangeCount > 0) {
|
|
const range = selection.getRangeAt(0);
|
|
const preCaretRange = range.cloneRange();
|
|
preCaretRange.selectNodeContents(element);
|
|
preCaretRange.setEnd(range.endContainer, range.endOffset);
|
|
position = preCaretRange.toString().length;
|
|
}
|
|
return position;
|
|
};
|
|
|
|
// 设置光标位置
|
|
setCaretPosition = (element, position) => {
|
|
const range = document.createRange();
|
|
const selection = window.getSelection();
|
|
console.log(element.childNodes[0], element, position);
|
|
range.setStart(element, position);
|
|
range.collapse(true);
|
|
selection.removeAllRanges();
|
|
selection.addRange(range);
|
|
};
|
|
|
|
// 高亮当前选中的建议项
|
|
highlightSuggestion = () => {
|
|
const suggestionItems = $node.querySelectorAll(".suggestion-item");
|
|
suggestionItems.forEach((item, index) => {
|
|
item.style.backgroundColor =
|
|
index === currentSelectionIndex ? "#f0f0f0" : "";
|
|
});
|
|
};
|
|
</script>
|
|
|
|
</html>
|