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

feat:添加链接展示方式选择

上级 029a67ee
......@@ -458,12 +458,10 @@ export default {
const url = item.csdn
? "https://link.csdn.net/?target=" + item.url
: item.url;
// linkEl.id = "md_link_card";
linkEl.className = "md_link_card";
// linkEl.className = "md_link_card";
linkEl.setAttribute("target", "_blank");
linkEl.setAttribute("href", url);
const title = getLinkTitle(linkEl, item);
linkEl.innerHTML = renderLinkCard(title, item);
});
_this.html = vDom.innerHTML;
......
import marked from "marked";
// import videojs from "video.js";
// import "video.js/dist/video-js.min.css";
// 获取选中文本信息
export function getSelectionInfo(selectorId) {
......@@ -372,9 +369,11 @@ export function getLinkTags(id, html) {
virtualDom.querySelectorAll("a:not([download])")
).map((item, index) => {
item.id = id + "_" + new Date().getTime() + "_" + index;
const linkType = item.getAttribute("data-type");
return {
id: item.id,
title: item.innerText,
linkType,
url: item.href
};
});
......@@ -394,30 +393,40 @@ export function removeLinkHeadAndEnd(link) {
}
export function renderLinkCard(title, item) {
console.log(item.title);
console.log(item.url);
return `
${
removeLinkHeadAndEnd(item.title) === removeLinkHeadAndEnd(item.url)
? ""
: `
<span class="md_link_title">${title || item.title || ""}</span>
`
}
${`<span class="md_link_desc" style="${
item.description ? "" : "margin: 0px 0 2px"
}">${item.description || ""}</span>`}
<span class="md_flex_card">
${
item.icon
? `<img class="md_link_img" referrerpolicy="no-referrer" id="md_link_img" src="${item.icon}" />`
: "<span class='md_link_img icon iconfont icon-lianjie'></span>"
const linkType = item.linkType;
let content;
switch (linkType) {
case "1":
content = `<span class="md_link_url">${item.title}</span>`;
break;
case "2":
content = `<span class="md_link_url">${item.url}</span>`;
break;
default:
content = `<div class="md_link_card">
${
removeLinkHeadAndEnd(item.title) === removeLinkHeadAndEnd(item.url)
? ""
: `
<span class="md_link_title">${title || item.title || ""}</span>
`
}
${`<span class="md_link_desc" style="${
item.description ? "" : "margin: 0px 0 2px"
}">${item.description || ""}</span>`}
<span class="md_flex_card">
${
item.icon
? `<img class="md_link_img" referrerpolicy="no-referrer" id="md_link_img" src="${item.icon}" />`
: "<span class='md_link_img icon iconfont icon-lianjie'></span>"
}
<span class="flex-1">
<span class="md_link_url">${item.url}</span>
</span>
</span></div>`;
break;
}
<span class="flex-1">
<span class="md_link_url">${item.url}</span>
</span>
</span>`;
return content;
}
export function preventDefault(id) {
......@@ -429,26 +438,6 @@ export function preventDefault(id) {
return;
}
// export async function renderVideo(id, html) {
// const virtualDom = document.createElement("div");
// virtualDom.innerHTML = html;
// document.body.appendChild(virtualDom);
// const videoList = Array.from(virtualDom.getElementsByTagName("video"));
// const newHtml = await new Promise((resolve, rej) => {
// videoList.forEach(item => {
// console.log(item);
// item.setAttribute("controls", "controls");
// });
// setTimeout(() => {
// const newHtml = virtualDom.innerHTML;
// resolve(newHtml);
// document.body.removeChild(virtualDom);
// }, 5000);
// });
// return newHtml;
// }
// 获取方法参数名
export function getParameterName(fn) {
if (typeof fn !== "object" && typeof fn !== "function") return;
......
......@@ -59,6 +59,10 @@
}
}
.md_link_url {
color: var(--md-editor-border-color-active);
}
#md_file_card,
.md_file_card {
display: block;
......
......@@ -104,11 +104,11 @@
}
.icon-help:before {
content: "\e503";
content: "\e641";
}
.icon-file:before {
content: "\e607";
content: "\e6c0";
}
.icon-doc:before {
......@@ -141,3 +141,7 @@
.icon-loading:before {
content: "\e626";
}
.icon-kapian:before {
content: "\e64c";
}
<template>
<div
class="md_select_container"
:style="{ left: this.left + 'px', top: this.top + 'px' }"
>
<ul v-show="list.length" class="md_select_user">
<li class="choose_title">显示为</li>
<li
@click="selectLinkType(index)"
v-for="(item, index) in list"
:key="index"
:id="linkTypeItemId(index)"
:class="[{ active: isActive(index) }]"
>
<span
:class="['md_select_user_avatar icon iconfont', `icon-${item.icon}`]"
></span>
<div class="md_select_user_info">
<div class="md_select_user_name">{{ item.nickname }}</div>
</div>
</li>
</ul>
</div>
</template>
<script>
export default {
props: {
position: {
type: Object,
default: () => {
return {
left: 0,
top: 0
};
}
},
activeLinkTypeIndex: {
type: Number,
default: 0
}
},
watch: {
position: {
immediate: true,
handler: function({ left, top }) {
this.left = left + 12;
this.top = top + 40;
}
}
},
data() {
return {
left: 0,
top: 0,
list: [
{
avatar: "",
icon: "kapian",
nickname: "卡片"
},
{
avatar: "",
icon: "biaoti",
nickname: "标题"
},
{
avatar: "",
icon: "lianjie",
nickname: "链接"
}
],
selectDisable: false
};
},
methods: {
selectLinkType(index) {
if (this.selectDisable) return;
this.selectDisable = true;
this.$emit("selectLinkType", index);
setTimeout(() => {
this.selectDisable = false;
}, 1000);
},
isActive(index) {
return index === this.activeLinkTypeIndex;
},
linkTypeItemId(index) {
return "md_link_type_id_" + index;
}
}
};
</script>
<style lang="less" scoped>
.md_select_container {
position: absolute;
background: var(--md-editor-content-bg-color);
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;
.md_select_user {
max-height: 196px;
width: 120px;
padding: 6px 0;
box-sizing: border-box;
overflow-y: auto;
overflow-x: hidden;
scrollbar-color: transparent transparent;
scrollbar-width: none;
margin: 0 !important;
list-style: none !important;
scroll-behavior: smooth;
.choose_title {
color: var(--md-editor-text-color-active);
font-size: 12px;
}
&::-webkit-scrollbar {
display: none;
}
&::-webkit-scrollbar-thumb {
border-radius: 1em;
background-color: rgba(50, 50, 50, 0.3);
}
&::-webkit-scrollbar-track {
border-radius: 1em;
background-color: rgba(50, 50, 50, 0);
}
li {
display: flex;
box-sizing: border-box;
padding: 4px 8px;
cursor: pointer;
@media (any-hover: hover) {
&:hover {
background: var(--md-editor-item-active-bg-color);
}
}
&.active {
background: var(--md-editor-item-active-bg-color);
}
.md_select_user_avatar {
width: 24px;
height: 24px;
font-size: 16px;
text-align: center;
line-height: 24px;
border-radius: 50%;
object-fit: cover;
margin-right: 6px;
}
.md_select_user_info {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-around;
align-items: flex-start;
.md_select_user_name {
font-size: 14px;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
color: var(--md-editor-text-color-active);
}
}
}
}
}
</style>
......@@ -10,8 +10,10 @@
@focus="setFocus(true)"
@blur="blur"
@paste="pasteFile"
@keydown.stop.up="changeActiveUserIndex($event, 'up')"
@keydown.stop.down="changeActiveUserIndex($event, 'down')"
@keydown.stop.left="handlePointMove"
@keydown.stop.right="handlePointMove"
@keydown.stop.up="changeActiveSelectIndex($event, 'up')"
@keydown.stop.down="changeActiveSelectIndex($event, 'down')"
@keydown.enter="handleEnter"
@keydown.meta.enter.exact="submit"
@keydown.ctrl.enter.exact="submit"
......@@ -42,15 +44,23 @@
:showHelp.sync="showHelp"
/>
</transition>
<transition name="slideup-fade">
<transition-group name="slideup-fade">
<selectUser
key="selectUser"
:userList="userList"
:activeUserIndex.sync="activeUserIndex"
v-show="showSelectUser"
:position="selectUserPosition"
@selectUser="handleSelectUser"
/>
</transition>
<selectLinkType
key="selectLinkType"
:activeLinkTypeIndex.sync="activeLinkTypeIndex"
:position="selectLinkTypePosition"
v-show="showSelectLinkType"
@selectLinkType="handleSelectLinkType"
/>
</transition-group>
</div>
</template>
<script>
......@@ -60,13 +70,15 @@ import {
isAndroid,
throttle as throttleFn
} from "@/assets/js/utils";
import selectUser from "./components/user-select";
import helpDoc from "./components/help-doc";
import selectUser from "./components/user-select.vue";
import selectLinkType from "./components/link-type-select.vue";
import helpDoc from "./components/help-doc.vue";
import renderMix from "./mixins/render-mixins";
import selectUserMix from "./mixins/select-user-mixins";
import selectLinkTypeMix from "./mixins/select-link-type-mixins";
export default {
components: { helpDoc, selectUser },
mixins: [renderMix, selectUserMix],
components: { helpDoc, selectUser, selectLinkType },
mixins: [renderMix, selectUserMix, selectLinkTypeMix],
props: {
id: {
type: String,
......@@ -142,6 +154,7 @@ export default {
editorHeight: "auto",
editorOverFlow: "auto",
showSelectUser: false,
showSelectLinkType: false,
queryInfo: {
startPosition: "",
endPosition: "",
......@@ -149,7 +162,9 @@ export default {
},
waiting: false,
activeUserIndex: 0,
selectUserPosition: { left: 0, top: 0 }
activeLinkTypeIndex: 0,
selectUserPosition: { left: 0, top: 0 },
selectLinkTypePosition: { left: 0, top: 0 }
};
},
created() {
......@@ -172,6 +187,7 @@ export default {
} else {
setTimeout(() => {
this.showSelectUser = false;
this.showSelectLinkType = false;
}, 200);
}
}
......@@ -228,7 +244,7 @@ export default {
}
},
methods: {
changeActiveUserIndex(event, type) {
changeActiveSelectIndex(event, type) {
if (this.showSelectUser) {
event.preventDefault();
const max = this.userList.length;
......@@ -275,6 +291,20 @@ export default {
}
// .scrollIntoView({ behavior: "smooth" });
}
} else if (this.showSelectLinkType) {
event.preventDefault();
const max = 3;
if (type === "down") {
this.activeLinkTypeIndex++;
if (this.activeLinkTypeIndex >= max) {
this.activeLinkTypeIndex = 0;
}
} else {
this.activeLinkTypeIndex--;
if (this.activeLinkTypeIndex < 0) {
this.activeLinkTypeIndex = max - 1;
}
}
}
},
resetPreviewMinHeight() {
......@@ -297,6 +327,9 @@ export default {
this.createSelectUserDialog("android");
}
if (this.showSelectUser) this.handleQueryUser(e);
if (this.showSelectLinkType) {
this.showSelectLinkType = false;
}
this.$emit("update:textLength", this.textContent.length);
this.emitText();
},
......@@ -345,12 +378,22 @@ export default {
this.autoSize && !this.fullScreen && !this.height ? "hidden" : "auto";
textEl.parentNode.removeChild(hideEl);
},
handlePointMove() {
if (this.showSelectLinkType) {
this.showSelectLinkType = false;
return;
}
},
handleEnter(e) {
if (this.showSelectUser) {
const activeUser = this.userList[this.activeUserIndex];
this.handleSelectUser(activeUser);
e.preventDefault();
return;
} else if (this.showSelectLinkType) {
this.handleSelectLinkType(this.activeLinkTypeIndex);
e.preventDefault();
return;
}
this.$emit("enter");
},
......@@ -381,6 +424,19 @@ export default {
fileList.push(items[i].getAsFile());
break;
}
if (items[i].type.indexOf("text") !== -1) {
items[i].getAsString(str => {
if (
!/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/.test(
str
)
)
return;
this.createSelectLinkTypeDialog();
});
break;
}
}
if (!fileList.length) return;
this.checkSelection();
......
......@@ -117,9 +117,14 @@ export default {
return out;
},
link(href, title, text) {
if (!href && !title) return "";
if (href === null) {
return text;
}
const linkTypeArr = href.split("::");
const linkType = linkTypeArr[1];
href = linkTypeArr[0];
text = text.split("::")[0];
let invalidText = "";
if (href === text) {
const invalidRule = /[)】}\]]*$/;
......@@ -134,6 +139,9 @@ export default {
if (title) {
out += ' title="' + title + '"';
}
if (linkType) {
out += ' data-type="' + linkType + '"';
}
out += ">" + text + "</a>";
if (invalidText) {
out += invalidText;
......
import { getPosition, formatText } from "@/assets/js/utils";
export default {
methods: {
handleSelectLinkType(index) {
console.log(index);
const originalText = this.textContent;
const queryInfo = this.queryInfo;
const cursorPosition = getPosition(this.id);
let typeStr = "";
switch (index) {
case 0:
break;
case 1:
typeStr = "::1";
break;
case 2:
typeStr = "::2";
break;
default:
break;
}
const newText =
originalText.slice(0, cursorPosition) +
typeStr +
originalText.slice(cursorPosition);
this.textContent = newText;
this.emitText();
this.showSelectLinkType = false;
this.$nextTick(() => {
const textEl = document.getElementById(this.id);
textEl.setSelectionRange(
cursorPosition + typeStr.length,
cursorPosition + typeStr.length
);
textEl.focus();
});
},
createSelectLinkTypeDialog(type) {
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");
const frameWidth = textEl.parentNode.offsetWidth;
this.selectLinkTypePosition = {
left:
pEl.offsetLeft < frameWidth * (2 / 3)
? pEl.offsetLeft
: pEl.offsetLeft - 200,
top: pEl.offsetTop - textEl.scrollTop
};
textEl.parentNode.removeChild(hideEl);
// this.queryInfo.startPosition = getPosition(this.id) + 1;
// this.queryInfo.endPosition = getPosition(this.id) + 1;
// if (type === "android") {
// this.queryInfo.startPosition--;
// this.queryInfo.endPosition--;
// }
// this.$emit("queryUserList", this.queryInfo.keyWord);
this.$nextTick(() => {
// const userList = this.userList;
// if (userList === false) return;
// if (this.queryInfo.keyWord === "") {
// this.allUserList = userList;
// }
this.showSelectLinkType = true;
this.$nextTick(() => {
const list = textEl.parentNode.querySelector(".md_select_user");
if (list) {
this.activeLinkTypeIndex = 0;
list.scrollTo(0, 0);
}
});
});
});
}
}
};
......@@ -46,7 +46,6 @@ export default {
},
created() {
setTimeout(() => {
// this.dialogPositon = this.getDialogPosition();
this.createBlockPoint();
}, 0);
},
......@@ -69,25 +68,13 @@ export default {
});
return res;
},
// getDialogPosition() {
// const dialog = this.$refs.block_container.parentElement;
// const transform = document.defaultView.getComputedStyle(dialog).transform;
// const list = transform.split(",");
// console.log(document.body);
// return { y: parseInt(list.pop()), x: parseInt(list.pop()) };
// },
createBlockPoint() {
const list = this.$refs.block;
this.list = this.list.map((item, index) => {
// item.x = list[index].offsetLeft;
item.x = list[index].getBoundingClientRect().left;
// item.y = list[index].offsetTop;
item.y = list[index].getBoundingClientRect().top;
return item;
});
// console.log(this.list);
},
getCursorPoint(e) {
this.mousePoint = {
......
......@@ -15,6 +15,7 @@
<!-- <span :class="['icon loading iconfont', `icon-${info.icon}`]"> </span> -->
</div>
</transition-group>
<!-- 自定义的工具 -->
<div
class="tool_button"
@click="info.click"
......@@ -346,10 +347,6 @@ export default {
color: var(--md-editor-border-color-active);
}
}
// &.loading {
// animation: rotate 3s linear infinite;
// color: var(--md-editor-border-color-active);
// }
&.icon-quxiaoquanping_o {
font-size: 24px;
margin: 0 -4px;
......@@ -364,7 +361,6 @@ export default {
&.icon-lianjie {
font-size: 16px;
}
&.icon-help,
&.icon-file {
font-size: 19px;
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册