提交 6cd70f38 编写于 作者: 璃白.'s avatar 璃白. 🌻

fix:优化@用户逻辑

上级 a6571f9f
......@@ -145,7 +145,7 @@ void main()
new Promise((res, rej) => {
setTimeout(() => {
res(file);
}, 2000);
}, 1000);
}).then(res => {
var reader = new FileReader();
reader.readAsDataURL(res);
......@@ -156,6 +156,7 @@ void main()
},
renderLinks: function(val, callback) {
const newLinks = val.map(item => {
item.csdn = true;
item.title =
"接口返回的标题接口返回的标题接口返回的标题接口返回的标题接口返回的标题接口返回的标题接口返回的标题接口返回的标题接口返回的标题";
item.img =
......@@ -166,8 +167,8 @@ void main()
});
setTimeout(() => {
callback(newLinks);
}, 2000);
},
}, 1000);
}
// queryUserList: function(val, callback) {
// const list = [
// {
......
此差异已折叠。
......@@ -396,8 +396,12 @@ export default {
console.log("返回的列表", res);
res.forEach(item => {
const linkEl = vDom.querySelector("#" + item.id);
const url = item.csdn
? "https://link.csdn.net/?target=" + item.url
: item.url;
linkEl.className = "md_link_card";
linkEl.setAttribute("target", "_blank");
linkEl.setAttribute("href", url);
const title = getLinkTitle(linkEl);
linkEl.innerHTML = `
<span class="md_link_title">${title || item.title}</span>
......
......@@ -29,7 +29,7 @@ export const throttle = function(fn, wait) {
}
};
};
// 获取光标的位置
export const getPosition = function(selectorId) {
const element = document.getElementById(selectorId);
if (!element) return 0;
......@@ -336,33 +336,11 @@ export function getLinkTitle(linkEl) {
return /^http/.test(title) ? "" : title;
}
export function rerender() {
const renderer = {
image(href, title, text) {
if (href === null) {
return text;
}
// ![file](...)渲染文件,只可以下载
if (text === "file") {
return `<a href="${href}" class="md_file_card md_flex_card" download target="_blank">
<span class="md_file_img icon iconfont icon-doc"></span>
<span class="flex-1">
<span class="md_file_title">${title}</span>
<span class="md_file_desc">16.6KB</span>
</span>
<span class="md_file_controls">
<span class="md_file_download icon iconfont icon-xiazai"></span>
</span>
</a>`;
}
// ![img](...)渲染图片
let out = '<img src="' + href + '" alt="' + text + '"';
if (title) {
out += ' title="' + title + '"';
}
out += "/>";
return out;
}
};
marked.use({ renderer });
export function preventDefault(id) {
const textEl = document.getElementById(id);
textEl.blur();
setTimeout(() => {
textEl.focus();
}, 0);
return;
}
......@@ -6,9 +6,11 @@
border: 1px solid var(--md-editor-border-color);
background: #f5f7fa;
transition: border 0.3s;
max-width: 800px;
&:hover {
border: 1px solid var(--md-editor-border-color-active);
max-width: 600px;
@media (any-hover: hover) {
&:hover {
border: 1px solid var(--md-editor-border-color-active);
}
}
span {
color: var(--md-editor-text-color-active);
......@@ -55,28 +57,35 @@
border: 1px solid var(--md-editor-border-color);
background: #f5f7fa;
transition: border 0.3s;
max-width: 600px;
&:hover {
border: 1px solid var(--md-editor-border-color-active);
max-width: 400px;
margin: 4px 0;
@media (any-hover: hover) {
&:hover {
border: 1px solid var(--md-editor-border-color-active);
}
}
span {
color: var(--md-editor-text-color-active);
}
.md_file_img {
font-size: 36px;
font-size: 34px;
margin-right: 16px;
color: var(--md-editor-text-color);
color: var(--md-editor-border-color-active);
}
.md_file_download {
font-size: 16px;
font-size: 14px;
padding: 8px;
box-sizing: border-box;
border: 1px solid var(--md-editor-border-color);
border-radius: 50%;
cursor: pointer;
transition: color 0.3s;
&:hover {
color: var(--md-editor-border-color-active);
line-height: 1 !important;
color: var(--md-editor-border-color);
@media (any-hover: hover) {
&:hover {
color: var(--md-editor-border-color-active);
}
}
}
.md_file_title {
......
......@@ -112,7 +112,7 @@
}
.icon-doc:before {
content: "\e642";
content: "\e689";
}
.icon-xiazai:before {
......
......@@ -3,11 +3,12 @@
class="md_select_container"
:style="{ left: this.left + 'px', top: this.top + 'px' }"
>
<ul v-show="userList.length" class="md_select_user">
<ul v-show="list.length" class="md_select_user">
<li
@click="selectUser(item)"
v-for="(item, index) in userList"
v-for="(item, index) in list"
:key="index"
:class="[{ active: isActive(index) }]"
>
<img class="md_select_user_avatar" :src="item.avatar" />
<div class="md_select_user_info">
......@@ -16,8 +17,8 @@
</div>
</li>
</ul>
<div v-show="userList.length" class="after"></div>
<div v-show="!userList.length" class="md_select_no_data">
<div v-show="list.length" class="after"></div>
<div v-show="!list.length" class="md_select_no_data">
轻敲空格完成输入
</div>
</div>
......@@ -37,6 +38,24 @@ export default {
userList: {
type: Array,
default: () => []
},
activeUserIndex: {
type: Number,
default: 0
}
},
computed: {
list() {
const list = this.userList;
if (!list.length) return [];
return list.map((item, index) => {
if (index === 0) {
item.active = true;
} else {
item.active = false;
}
return item;
});
}
},
watch: {
......@@ -57,6 +76,9 @@ export default {
methods: {
selectUser(user) {
this.$emit("selectUser", user);
},
isActive(index) {
return index === this.activeUserIndex;
}
}
};
......@@ -86,12 +108,6 @@ export default {
.md_select_user {
max-height: 214px;
width: 180px;
// background: #fff;
// box-shadow: 0 1px 6px rgb(0 0 0 / 10%);
// border: 1px solid var(--md-editor-border-color);
// border-radius: 4px;
// z-index: var(--md-editor-fullScrren-zIndex);
// margin: 0;
padding: 6px 0;
box-sizing: border-box;
overflow-y: auto;
......@@ -115,7 +131,8 @@ export default {
box-sizing: border-box;
padding: 6px 8px;
cursor: pointer;
&:hover {
&:hover,
&.active {
background: #f5f7fa;
}
// & + li {
......
......@@ -8,7 +8,9 @@
@focus="setFocus(true)"
@blur="setFocus(false)"
@paste="pasteFile"
@keydown.stop.50="keyup"
@keydown.stop.50="handleCallUser"
@keydown.stop.up.prevent="changeActiveUserIndex('up')"
@keydown.stop.down.prevent="changeActiveUserIndex('down')"
@keydown.enter="handleEnter"
@keydown.meta.enter.exact="submit"
@keydown.ctrl.enter.exact="submit"
......@@ -39,9 +41,10 @@
<transition name="slideup-fade">
<selectUser
:userList="userList"
:activeUserIndex.sync="activeUserIndex"
v-show="showSelectUser"
:position="selectUserPosition"
@selectUser="selectUser"
@selectUser="handleSelectUser"
/>
</transition>
</div>
......@@ -50,19 +53,16 @@
import {
getSelectionInfo,
getPosition,
getFilteredTags,
getLinkTags,
formatText,
rerender,
addLanguageClass,
preventDefault,
throttle as throttleFn
} from "@/assets/js/utils";
import marked from "marked";
import selectUser from "./components/user-select";
import helpDoc from "./components/help-doc";
import DOMPurify from "dompurify";
import renderMix from "./mixins/render-mixins";
import selectUserMix from "./mixins/select-user-mixins";
export default {
components: { helpDoc, selectUser },
mixins: [renderMix, selectUserMix],
props: {
id: {
type: String,
......@@ -143,6 +143,7 @@ export default {
endPosition: "",
keyWord: ""
},
activeUserIndex: 0,
selectUserPosition: { left: 0, top: 0 }
};
},
......@@ -158,7 +159,9 @@ export default {
if (val) {
this.resetPreviewMinHeight();
} else {
this.showSelectUser = false;
setTimeout(() => {
// this.showSelectUser = false;
}, 200);
}
}
},
......@@ -214,6 +217,22 @@ export default {
}
},
methods: {
changeActiveUserIndex(type) {
if (this.showSelectUser) {
const max = this.userList.length;
if (type === "down") {
this.activeUserIndex++;
if (this.activeUserIndex >= max) {
this.activeUserIndex = 0;
}
} else {
this.activeUserIndex--;
if (this.activeUserIndex < 0) {
this.activeUserIndex = max - 1;
}
}
}
},
resetPreviewMinHeight() {
setTimeout(() => {
const textEl = document.getElementById(this.id);
......@@ -229,130 +248,11 @@ export default {
keyWord: ""
};
},
transferMarkdown(val) {
rerender();
marked.setOptions({
breaks: true,
gfm: true,
langPrefix: "language-",
highlight: function(code, lang, callback) {
let html = require("highlight.js").highlightAuto(code).value;
return html;
}
});
const str = val + "";
const html = marked(str); // 解析markdown
const virtualDom = addLanguageClass(html); // 如果没指定语言,添加默认语言
const cleanHtml = DOMPurify.sanitize(virtualDom.innerHTML, {
FORBID_TAGS: [
"style",
"script",
"select",
"option",
"input",
"textarea",
"form",
"button"
]
}); // 去除标签
const filteredTags = getFilteredTags(html, cleanHtml); // 计算是否有标签被过滤
// 链接转换为卡片
const { vDom, links } = getLinkTags(this.id, cleanHtml);
this.$emit("getFilteredTags", filteredTags);
this.$emit("update:html", cleanHtml);
if (links.length) this.$emit("renderLinksHtml", { vDom, links });
},
input() {
if (this.showSelectUser) this.handleQueryUser();
this.$emit("update:textLength", this.textContent.length);
this.emitText();
},
selectUser(user) {
const originalText = this.textContent;
const queryInfo = this.queryInfo;
const cursorPosition = getPosition(this.id);
const username = user.name + " ";
const newText =
originalText.slice(0, queryInfo.startPosition) +
username +
originalText.slice(queryInfo.endPosition);
this.textContent = newText;
this.emitText();
this.showSelectUser = false;
this.$nextTick(() => {
const textEl = document.getElementById(this.id);
textEl.setSelectionRange(
cursorPosition + username.length,
cursorPosition + username.length
);
textEl.focus();
});
},
handleQueryUser() {
const endPosition = getPosition(this.id);
const startPosition = this.queryInfo.startPosition;
const keyWord = this.textContent.slice(startPosition, endPosition);
this.queryInfo.endPosition = endPosition;
if (endPosition < startPosition || keyWord.slice(-1) === " ") {
this.showSelectUser = false;
return;
}
this.queryInfo.keyWord = keyWord;
this.$emit("queryUserList", keyWord);
},
keyup(e) {
if (e.key === "@") {
this.createSelectUserDialog();
}
},
createSelectUserDialog() {
const textEl = document.getElementById(this.id);
if (!textEl) return;
const height = getComputedStyle(textEl).getPropertyValue("height");
const width = getComputedStyle(textEl).getPropertyValue("width");
const scrollTop = textEl.scrollTop;
const originalText = this.textContent;
const cursorPoint = getPosition(this.id);
const selectionInfo = {
selectionStart: cursorPoint,
selectionEnd: cursorPoint
};
const newText = formatText(
originalText,
selectionInfo,
"<span id='call_position'>",
"</span>"
);
const hideEl = this.createHideEl("clac_position_El_");
hideEl.style.position = "absolute";
hideEl.style.width = width;
hideEl.style.height = height;
hideEl.style.overflowY = "auto";
hideEl.style.wordBreak = "break-all";
hideEl.style.top = "14px";
hideEl.style.left = 0;
hideEl.style.whiteSpace = "pre-wrap";
hideEl.innerHTML = newText;
this.$nextTick(() => {
hideEl.scrollTop = scrollTop;
const pEl = document.getElementById("call_position");
this.selectUserPosition = {
left: pEl.offsetLeft,
top: pEl.offsetTop - textEl.scrollTop
// left: pEl.getBoundingClientRect().left,
// top: pEl.getBoundingClientRect().top
};
textEl.parentNode.removeChild(hideEl);
this.showSelectUser = true;
this.queryInfo.startPosition = getPosition(this.id) + 1;
this.queryInfo.endPosition = getPosition(this.id) + 1;
this.$emit("queryUserList", this.queryInfo.keyWord);
});
},
createHideEl(type) {
const textEl = document.getElementById(this.id);
if (!textEl) return;
......@@ -394,14 +294,11 @@ export default {
this.autoSize && !this.fullScreen && !this.height ? "hidden" : "auto";
textEl.parentNode.removeChild(hideEl);
},
handleEnter() {
handleEnter(e) {
if (this.showSelectUser) {
const textEl = document.getElementById(this.id);
textEl.blur();
setTimeout(() => {
textEl.focus();
// this.showSelectUser = false;
}, 0);
const activeUser = this.userList[this.activeUserIndex];
this.handleSelectUser(activeUser);
e.preventDefault();
return;
}
this.$emit("enter");
......
import {
getFilteredTags,
getLinkTags,
addLanguageClass
} from "@/assets/js/utils";
import marked from "marked";
import DOMPurify from "dompurify";
export default {
methods: {
transferMarkdown(val) {
this.rerender();
marked.setOptions({
breaks: true,
gfm: true,
langPrefix: "language-",
highlight: function(code, lang, callback) {
let html = require("highlight.js").highlightAuto(code).value;
return html;
}
});
const str = val + "";
const html = marked(str); // 解析markdown
const virtualDom = addLanguageClass(html); // 如果没指定语言,添加默认语言
const cleanHtml = DOMPurify.sanitize(virtualDom.innerHTML, {
FORBID_TAGS: [
"style",
"script",
"select",
"option",
"input",
"textarea",
"form",
"button"
]
}); // 去除标签
const filteredTags = getFilteredTags(html, cleanHtml); // 计算是否有标签被过滤
// 链接转换为卡片
const { vDom, links } = getLinkTags(this.id, cleanHtml);
this.$emit("getFilteredTags", filteredTags);
this.$emit("update:html", cleanHtml);
if (links.length) this.$emit("renderLinksHtml", { vDom, links });
},
rerender() {
const renderer = {
image(href, title, text) {
if (href === null) {
return text;
}
// ![file](...)渲染文件,只可以下载
if (text === "file") {
return `<div class="md_file_card md_flex_card">
<span class="md_file_img icon iconfont icon-doc"></span>
<span class="flex-1">
<span class="md_file_title">${title}</span>
<span class="md_file_desc">16.6KB</span>
</span>
<span class="md_file_controls">
<a href="${href}" download target="_blank" class="md_file_download icon iconfont icon-xiazai"></a>
</span>
</div>`;
}
// ![img](...)渲染图片
let out = '<img src="' + href + '" alt="' + text + '"';
if (title) {
out += ' title="' + title + '"';
}
out += "/>";
return out;
}
};
marked.use({ renderer });
}
}
};
import { getPosition, formatText } from "@/assets/js/utils";
export default {
methods: {
handleSelectUser(user) {
const originalText = this.textContent;
const queryInfo = this.queryInfo;
const cursorPosition = getPosition(this.id);
const username = user.name + " ";
const newText =
originalText.slice(0, queryInfo.startPosition) +
username +
originalText.slice(queryInfo.endPosition);
this.textContent = newText;
this.emitText();
this.showSelectUser = false;
this.$nextTick(() => {
const textEl = document.getElementById(this.id);
textEl.setSelectionRange(
cursorPosition + username.length,
cursorPosition + username.length
);
textEl.focus();
});
},
handleQueryUser() {
const endPosition = getPosition(this.id);
const startPosition = this.queryInfo.startPosition;
const keyWord = this.textContent.slice(startPosition, endPosition);
this.queryInfo.endPosition = endPosition;
if (endPosition < startPosition || keyWord.slice(-1) === " ") {
this.showSelectUser = false;
return;
}
this.queryInfo.keyWord = keyWord;
this.$emit("queryUserList", keyWord);
},
handleCallUser(e) {
if (e.key === "@") {
this.createSelectUserDialog();
}
},
createSelectUserDialog() {
const textEl = document.getElementById(this.id);
if (!textEl) return;
const height = getComputedStyle(textEl).getPropertyValue("height");
const width = getComputedStyle(textEl).getPropertyValue("width");
const scrollTop = textEl.scrollTop;
const originalText = this.textContent;
const cursorPoint = getPosition(this.id);
const selectionInfo = {
selectionStart: cursorPoint,
selectionEnd: cursorPoint
};
const newText = formatText(
originalText,
selectionInfo,
"<span id='call_position'>",
"</span>"
);
const hideEl = this.createHideEl("clac_position_El_");
hideEl.style.position = "absolute";
hideEl.style.width = width;
hideEl.style.height = height;
hideEl.style.overflowY = "auto";
hideEl.style.wordBreak = "break-all";
hideEl.style.top = "14px";
hideEl.style.left = 0;
hideEl.style.whiteSpace = "pre-wrap";
hideEl.innerHTML = newText;
this.$nextTick(() => {
hideEl.scrollTop = scrollTop;
const pEl = document.getElementById("call_position");
this.selectUserPosition = {
left: pEl.offsetLeft,
top: pEl.offsetTop - textEl.scrollTop
// left: pEl.getBoundingClientRect().left,
// top: pEl.getBoundingClientRect().top
};
textEl.parentNode.removeChild(hideEl);
this.showSelectUser = true;
this.queryInfo.startPosition = getPosition(this.id) + 1;
this.queryInfo.endPosition = getPosition(this.id) + 1;
this.$emit("queryUserList", this.queryInfo.keyWord);
});
}
}
};
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册