+
匿名发布
@@ -56,14 +62,21 @@
-->
-
+
+
+
diff --git a/js/fontSize.js b/js/fontSize.js
index 9f735d0..6888062 100644
--- a/js/fontSize.js
+++ b/js/fontSize.js
@@ -13,7 +13,6 @@
function setRemUnit() {
var docFontSize = (docEl.clientWidth / sizeUI) * remBase;
- console.log("bodyEl", bodyEl);
docEl.style.fontSize = docFontSize + "px";
bodyEl.style.fontSize = 16 / docFontSize + "rem";
handleRemAdapt();
diff --git a/js/index.js b/js/index.js
index f242e6c..ec6531e 100644
--- a/js/index.js
+++ b/js/index.js
@@ -1,9 +1,12 @@
const { createApp, ref, onMounted, nextTick, onUnmounted, computed, watch } = Vue;
+
+// 聚焦正文时间 图片表情包等操作放在 键盘上
+
createApp({
setup() {
let titleLength = ref(200);
- let title = ref("发");
+ let title = ref("");
let info = ref({
anonymity: 0,
@@ -14,10 +17,9 @@ createApp({
const adjustTextareaHeight = () => {
nextTick(() => {
const textarea = titleTextarea.value;
- if (textarea) {
- textarea.style.height = "auto";
- textarea.style.height = textarea.scrollHeight + "px";
- }
+ if (!textarea) return;
+ textarea.style.height = "auto";
+ textarea.style.height = textarea.scrollHeight + "px";
});
};
@@ -29,15 +31,174 @@ createApp({
onMounted(() => {
document.addEventListener("selectionchange", getFocusedNodeName);
+ // 添加键盘事件监听
+ document.addEventListener("keydown", handleDeleteKey);
});
+ onUnmounted(() => {
+ document.removeEventListener("selectionchange", getFocusedNodeName);
+ document.removeEventListener("keydown", handleDeleteKey);
+ });
+
+ // 处理删除键和回车键事件
+ const handleDeleteKey = (e) => {
+ // 处理删除键(Backspace和Delete)
+ if (e.key === "Backspace" || e.key === "Delete") {
+ const selection = window.getSelection();
+ if (!selection.rangeCount) return;
+
+ const range = selection.getRangeAt(0);
+ let currentNode = range.startContainer;
+
+ // 如果是文本节点,取其父元素
+ if (currentNode.nodeType === Node.TEXT_NODE) {
+ currentNode = currentNode.parentNode;
+ }
+
+ // 检查当前节点或其祖先是否是span.blue元素
+ let blueSpan = null;
+ let tempNode = currentNode;
+
+ while (tempNode && tempNode !== editorRef.value) {
+ if (tempNode.classList && tempNode.classList.contains("blue")) {
+ blueSpan = tempNode;
+ break;
+ }
+ tempNode = tempNode.parentNode;
+ }
+
+ // 如果在span.blue内且按下删除键,阻止默认行为并删除整个span.blue元素
+ if (blueSpan) {
+ e.preventDefault();
+
+ // 删除整个span.blue元素
+ const parentNode = blueSpan.parentNode;
+ const rangeBefore = document.createRange();
+ rangeBefore.setStartBefore(blueSpan);
+ rangeBefore.setEndBefore(blueSpan);
+
+ // 保存删除前的位置,用于删除后设置光标
+ const startOffset = rangeBefore.toString().length;
+
+ // 删除元素
+ parentNode.removeChild(blueSpan);
+
+ // 设置光标到删除位置
+ const newRange = document.createRange();
+ const textNodes = [];
+ let currentTextNode = parentNode.firstChild;
+ let currentLength = 0;
+
+ while (currentTextNode && currentLength < startOffset) {
+ if (currentTextNode.nodeType === Node.TEXT_NODE) {
+ textNodes.push(currentTextNode);
+ currentLength += currentTextNode.nodeValue.length;
+ }
+ currentTextNode = currentTextNode.nextSibling;
+ }
+
+ if (textNodes.length > 0) {
+ const targetNode = textNodes[textNodes.length - 1];
+ const offset = Math.min(startOffset - (currentLength - targetNode.nodeValue.length), targetNode.nodeValue.length);
+ newRange.setStart(targetNode, offset);
+ newRange.setEnd(targetNode, offset);
+
+ selection.removeAllRanges();
+ selection.addRange(newRange);
+ lastSelection = newRange;
+ }
+ }
+ }
+ // 处理回车键
+ else if (e.key === "Enter") {
+ const selection = window.getSelection();
+ if (!selection.rangeCount) return;
+
+ const range = selection.getRangeAt(0);
+ let currentNode = range.startContainer;
+
+ // 如果是文本节点,取其父元素
+ if (currentNode.nodeType === Node.TEXT_NODE) {
+ currentNode = currentNode.parentNode;
+ }
+
+ // 检查当前节点或其祖先是否是span.blue元素
+ let blueSpan = null;
+ let tempNode = currentNode;
+
+ while (tempNode && tempNode !== editorRef.value) {
+ if (tempNode.classList && tempNode.classList.contains("blue")) {
+ blueSpan = tempNode;
+ break;
+ }
+ tempNode = tempNode.parentNode;
+ }
+
+ // 如果在span.blue内且按下回车键,阻止默认行为并将光标后面的内容提取到新的span.blue中
+ if (blueSpan && range.startContainer.nodeType === Node.TEXT_NODE) {
+ e.preventDefault();
+
+ // 获取当前文本节点和偏移量
+ const textNode = range.startContainer;
+ const offset = range.startOffset;
+ const textContent = textNode.nodeValue;
+
+ // 如果光标在文本末尾,不做任何处理
+ if (offset >= textContent.length) {
+ // 创建新的span.blue元素并插入到当前span后面
+ const newBlueSpan = document.createElement("span");
+ // newBlueSpan.className = "blue";
+
+ // 创建换行符
+ const br = document.createElement("br");
+ blueSpan.parentNode.insertBefore(br, blueSpan.nextSibling);
+ blueSpan.parentNode.insertBefore(newBlueSpan, br.nextSibling);
+
+ // 设置光标到新的span.blue元素内
+ const newRange = document.createRange();
+ newRange.setStart(newBlueSpan, 0);
+ newRange.setEnd(newBlueSpan, 0);
+ selection.removeAllRanges();
+ selection.addRange(newRange);
+ lastSelection = newRange;
+ } else {
+ // 分割文本节点
+ const newTextNode = textNode.splitText(offset);
+
+ // 创建新的span.blue元素
+ const newBlueSpan = document.createElement("span");
+ // newBlueSpan.className = "blue";
+
+ // 将分割后的文本节点移动到新的span.blue元素中
+ newBlueSpan.appendChild(newTextNode.cloneNode(true));
+
+ // 删除原分割后的文本节点
+ newTextNode.parentNode.removeChild(newTextNode);
+
+ // 创建换行符
+ const br = document.createElement("br");
+
+ // 插入换行符和新的span.blue元素
+ blueSpan.parentNode.insertBefore(br, blueSpan.nextSibling);
+ blueSpan.parentNode.insertBefore(newBlueSpan, br.nextSibling);
+
+ // 设置光标到新的span.blue元素内的文本开始位置
+ const newRange = document.createRange();
+ newRange.setStart(newBlueSpan.firstChild, 0);
+ newRange.setEnd(newBlueSpan.firstChild, 0);
+ selection.removeAllRanges();
+ selection.addRange(newRange);
+ lastSelection = newRange;
+ }
+ }
+ }
+ };
+
// 获取当前焦点所在的节点名称(仅在.editor内)
const getFocusedNodeName = () => {
const selection = window.getSelection();
if (!selection.rangeCount) return null;
- lastSelection = selection.getRangeAt(0);
-
// 获取焦点所在的节点
let focusedNode = selection.focusNode;
@@ -50,82 +211,229 @@ createApp({
const isInEditor = editorRef.value.contains(focusedNode);
if (!isInEditor) return null;
- if (focusedNode.tagName?.toLowerCase() == "h2") isPTitle.value = true;
- else isPTitle.value = false;
+ lastSelection = selection.getRangeAt(0);
+ console.log("更新选区");
+ updatePTitleStatus();
};
const isPTitle = ref(false);
+ // 初始化时设置lastSelection为第一个有效位置
let lastSelection = null;
- const onEditorInput = () => {
+ onMounted(() => {
+ setTimeout(() => {
+ focusLastNode();
+ }, 1000);
+ });
+
+ const focusLastNode = () => {
+ const editor = document.getElementById("editor");
+ const selection = window.getSelection();
+
+ // 清除现有选择范围
+ selection.removeAllRanges();
+
+ // 创建新的范围对象
+ const range = document.createRange();
+
+ // 找到最后一个有效子节点(跳过空白文本节点)
+ let lastNode = editor.lastChild;
+ while (lastNode) {
+ // 检查是否为有效节点(非空白文本节点)
+ if (!(lastNode.nodeType === 3 && lastNode.textContent.trim() === "")) {
+ break;
+ }
+ lastNode = lastNode.previousSibling;
+ }
+
+ if (lastNode) {
+ // 设置范围到最后一个节点的末尾
+ range.setStartAfter(lastNode);
+ range.setEndAfter(lastNode);
+ } else {
+ // 如果编辑器为空,选择整个编辑器
+ range.selectNodeContents(editor);
+ range.collapse(false);
+ }
+
+ // 将范围添加到选择对象,不设置焦点
+ selection.addRange(range);
+ };
+
+ let isEmpty = ref(true);
+
+ const onEditorInput = (event) => {
const selection = window.getSelection();
if (selection.rangeCount > 0) {
lastSelection = selection.getRangeAt(0);
+ console.log("更新选区");
updatePTitleStatus();
}
+
+ judgeIsEmpty();
+ };
+
+ // 判断是否为空
+ const judgeIsEmpty = () => {
+ const text = editorRef.value.innerText;
+ console.log("text", text);
+
+ isEmpty.value = text.length == 0 && !editorRef.value.querySelector("img");
};
const paragraphTitle = () => {
+ // 保存当前滚动位置
+ const scrollTop = window.scrollY;
+
editorRef.value.focus();
if (!lastSelection) return;
const selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(lastSelection);
- const focusNode = lastSelection.startContainer;
- document.execCommand("formatBlock", false, focusNode?.parentNode?.tagName == "H2" ? "P" : "H2");
+
+ // 使用try-catch确保即使命令执行失败也能恢复滚动位置
+ try {
+ document.execCommand("formatBlock", false, isPTitle.value ? "P" : "H2");
+ } catch (error) {
+ console.error("应用段落格式失败:", error);
+ }
+
+ // 立即恢复滚动位置
+ window.scrollTo(0, scrollTop);
+
+ // 更新状态
+ updatePTitleStatus();
};
const updatePTitleStatus = () => {
if (lastSelection) {
const node = lastSelection.commonAncestorContainer;
+ let parentElement = node.parentElement;
+
isPTitle.value = node.nodeName === "H2" || (node.nodeType === Node.TEXT_NODE && node.parentNode?.nodeName === "H2");
+
+ // 死循环,直到遇到终止条件
+ while (true) {
+ // 如果没有父元素了(到达文档根节点),退出循环返回false
+ if (!parentElement) {
+ isPTitle.value = false;
+ return;
+ }
+
+ // 遇到id为"editor"的元素,返回false
+ if (parentElement.id === "editor") {
+ isPTitle.value = false;
+ return;
+ }
+
+ // 遇到nodeName为"H2"的元素,返回true(注意nodeName是大写的)
+ if (parentElement.nodeName === "H2") {
+ isPTitle.value = true;
+ return;
+ }
+
+ // 继续向上查找父元素
+ parentElement = parentElement.parentElement;
+ }
}
};
- const insertImage = () => {
- const input = document.createElement("input");
- input.type = "file";
- input.accept = "image/*";
- input.onchange = (e) => {
- const file = e.target.files[0];
- const reader = new FileReader();
- reader.onload = (e) => {
- // 创建图片元素
+ const maxSize = 20 * 1024 * 1024; // 20MB
+
+ const insertImage = (event) => {
+ const file = event.target.files[0];
+
+ if (!file) return; // 处理未选择文件的情况
+
+ if (file.size > maxSize) {
+ console.log("文件大小不能超过 20MB");
+ return;
+ }
+
+ const reader = new FileReader();
+ reader.onload = (e) => {
+ const imgSrc = e.target.result;
+ console.log("imgSrc", imgSrc);
+
+ try {
+ const range = lastSelection;
const img = document.createElement("img");
- img.src = e.target.result;
- img.alt = "用户上传图片";
+ img.src = imgSrc;
+ range.insertNode(img);
+ const div = document.createElement("div");
+ range.insertNode(div);
- // 确保编辑器获得焦点
- editorRef.value.focus();
-
- // 获取选择对象
- const selection = window.getSelection();
-
- // 如果有选择范围,在选择范围插入图片
- console.log("rangeCount", selection.rangeCount);
-
- if (selection.rangeCount > 0) {
- const range = selection.getRangeAt(0);
- range.deleteContents(); // 清除当前选择内容
- range.insertNode(img); // 插入图片
-
- // 将光标移动到图片后面
- range.setStartAfter(img);
- range.setEndAfter(img);
- selection.removeAllRanges();
- selection.addRange(range);
- } else {
- // 如果没有选择范围,直接追加到编辑器末尾
- editorRef.value.appendChild(img);
- }
- };
- reader.readAsDataURL(file);
+ judgeIsEmpty();
+ } catch (error) {
+ console.error("插入图片出错:", error);
+ }
};
- input.click();
+ reader.readAsDataURL(file);
};
- return { editorRef, info, title, titleLength, titleTextarea, adjustTextareaHeight, isPTitle, paragraphTitle, insertImage, onEditorInput };
+ const insertLabel = (label) => {
+ const span = document.createElement("span");
+ span.innerHTML = `
#${label} `;
+ lastSelection.insertNode(span);
+
+ // 移动光标到emoji后面
+ lastSelection.setStartAfter(span);
+ lastSelection.setEndAfter(span);
+
+ judgeIsEmpty();
+ };
+
+ let emojiState = ref(false);
+
+ const optionEmoji = ref(["😀", "😁", "😆", "😅", "😂", "😉", "😍", "🥰", "😘", "🤥", "😪", "😵💫", "🤓", "🥺", "😋", "😜", "🤪", "😎", "🤩", "🥳", "😔", "🙁", "😭", "😡", "😳", "🤗", "🤔", "🤭", "🤫", "😯", "😵", "🙄", "🥴", "🤢", "🤑", "🤠", "👌", "✌️", "🤟", "🤘", "🤙", "👍", "👎", "✊", "👏", "🤝", "🙏", "💪", "❎️", "✳️", "✴️", "❇️", "#️⃣", "*️⃣", "1️⃣", "2️⃣", "3️⃣", "4️⃣", "5️⃣", "6️⃣", "7️⃣", "8️⃣", "9️⃣", "🔟", "🆗", "🈶", "🉐", "🉑", "🌹", "🥀", "🌸", "🌺", "🌷", "🌲", "☘️", "🍀", "🍁", "🌙", "⭐", "🌍", "☀️", "⭐️", "🌟", "☁️", "🌈", "☂️", "❄️", "☃️", "☄️", "🔥", "💧", "🍎", "🍐", "🍊", "🍉", "🍓", "🍑", "🍔", "🍟", "🍕", "🥪", "🍜", "🍡", "🍨", "🍦", "🎂", "🍰", "🍭", "🍿", "🍩", "🧃", "🍹", "🍒", "🥝", "🥒", "🥦", "🥨", "🌭", "🥘", "🍱", "🍢", "🥮", "🍩", "🍪", "🧁", "🍵", "🍶", "🍻", "🥂", "🧋", "🎉", "🎁", "🧧", "🎃", "🎄", "🧨", "✨️", "🎈", "🎊", "🎋", "🎍", "🎀", "🎖️", "🏆️", "🏅", "💌", "📬", "🚗", "🚕", "🚲", "🛵", "🚀", "🚁", "⛵", "🚢", "🔮", "🧸", "🀄️"]);
+
+ const openEmoji = () => (emojiState.value = true);
+
+ const closeEmoji = () => (emojiState.value = false);
+
+ const selectEmoji = (emoji) => {
+ const textNode = document.createTextNode(emoji);
+ lastSelection.insertNode(textNode);
+
+ // 移动光标到emoji后面
+ lastSelection.setStartAfter(textNode);
+ lastSelection.setEndAfter(textNode);
+
+ judgeIsEmpty();
+ getCursorPosition();
+ };
+
+ const getCursorPosition = () => {
+ const range = lastSelection;
+ const tempElement = document.createElement("span");
+ tempElement.classList.add("cursor");
+ // 在光标位置插入临时元素
+ range.insertNode(tempElement);
+
+ // 获取临时元素的位置信息
+ const rect = tempElement.getBoundingClientRect();
+
+ // 可视窗口高度
+ const viewportHeight = window.innerHeight;
+
+ // 计算目标位置:中间偏上(视口高度的30%位置)
+ // 公式:元素顶部相对于视口的位置 + 滚动距离 - 目标位置(视口高度的30%)
+ const targetPosition = window.scrollY + rect.top - viewportHeight * 0.3; // 30%位置,比正中间更靠上
+
+ // 平滑滚动到目标位置
+ window.scrollTo({
+ top: targetPosition,
+ behavior: "smooth", // 平滑滚动,移除则为瞬间滚动
+ });
+
+ // 移除临时元素
+ tempElement.parentNode.removeChild(tempElement);
+ };
+
+ const cutAnonymity = () => (info.value.anonymity = info.value.anonymity ? 0 : 1);
+
+ return { cutAnonymity, isEmpty, selectEmoji, closeEmoji, openEmoji, optionEmoji, emojiState, insertLabel, editorRef, info, title, titleLength, titleTextarea, adjustTextareaHeight, isPTitle, paragraphTitle, insertImage, onEditorInput };
},
}).mount("#appIndex");