Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
璃白.
markdown-editor
提交
61d8025d
M
markdown-editor
项目概览
璃白.
/
markdown-editor
上一次同步 接近 2 年
通知
1
Star
1
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
M
markdown-editor
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
61d8025d
编写于
8月 03, 2021
作者:
璃白.
🌻
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
fix:优化@用户样式
上级
e0e776dc
变更
10
隐藏空白更改
内联
并排
Showing
10 changed file
with
157 addition
and
102 deletion
+157
-102
src/App.vue
src/App.vue
+14
-12
src/assets/js/utils.js
src/assets/js/utils.js
+30
-1
src/assets/style/card.less
src/assets/style/card.less
+6
-4
src/assets/style/global.less
src/assets/style/global.less
+7
-1
src/components/content/components/user-select.vue
src/components/content/components/user-select.vue
+3
-3
src/components/content/md-textarea.vue
src/components/content/md-textarea.vue
+2
-2
src/components/content/mixins/render-mixins.js
src/components/content/mixins/render-mixins.js
+12
-6
src/components/content/mixins/select-user-mixins.js
src/components/content/mixins/select-user-mixins.js
+14
-5
src/components/header/md-header.vue
src/components/header/md-header.vue
+7
-7
src/main.js
src/main.js
+62
-61
未找到文件。
src/App.vue
浏览文件 @
61d8025d
...
...
@@ -68,6 +68,7 @@
@
updateShowHelp=
"showHelp = $event"
@
renderLinksHtml=
"renderLinksHtml"
@
queryUserList=
"queryUserList"
@
callUserList=
"callUserList = $event"
v-else
/>
<div
v-if=
"maxLength && showWordLimit && !showPreview"
class=
"word_limit"
>
...
...
@@ -88,7 +89,12 @@ import markdownHeader from "./components/header/md-header";
import
markdownFooter
from
"
./components/footer/md-footer
"
;
import
markdownEditor
from
"
./components/content/md-textarea
"
;
import
markdownPreview
from
"
./components/content/md-preview
"
;
import
{
formatText
,
checktUrl
,
getLinkTitle
}
from
"
@/assets/js/utils
"
;
import
{
formatText
,
checktUrl
,
getLinkTitle
,
renderLinkCard
}
from
"
@/assets/js/utils
"
;
export
default
{
components
:
{
markdownHeader
,
...
...
@@ -248,7 +254,8 @@ export default {
htmlMinHeight
:
150
,
showHelp
:
false
,
textLength
:
""
,
userList
:
[],
userList
:
false
,
callUserList
:
[],
selectionInfo
:
{
selectorId
:
""
,
selectionStart
:
""
,
...
...
@@ -315,6 +322,9 @@ export default {
const
checkResult
=
checktUrl
(
val
,
this
.
filePathRule
);
emitContent
.
invalidList
=
checkResult
;
}
if
(
this
.
callUserList
.
length
)
{
emitContent
.
callUserList
=
this
.
callUserList
;
}
emitContent
.
filteredTags
=
this
.
filteredTags
;
this
.
$emit
(
"
change
"
,
emitContent
);
this
.
$emit
(
"
input
"
,
emitContent
);
...
...
@@ -395,6 +405,7 @@ export default {
}).
then
(
res
=>
{
console
.
log
(
"
返回的列表
"
,
res
);
res
.
forEach
(
item
=>
{
item
.
csdn
=
true
;
const
linkEl
=
vDom
.
querySelector
(
"
#
"
+
item
.
id
);
const
url
=
item
.
csdn
?
"
https://link.csdn.net/?target=
"
+
item
.
url
...
...
@@ -403,16 +414,7 @@ export default {
linkEl
.
setAttribute
(
"
target
"
,
"
_blank
"
);
linkEl
.
setAttribute
(
"
href
"
,
url
);
const
title
=
getLinkTitle
(
linkEl
);
linkEl
.
innerHTML
=
`
<span class="md_link_title">
${
title
||
item
.
title
}
</span>
<span class="md_link_desc">
${
item
.
desc
}
</span>
<span class="md_flex_card">
<img class="md_link_img" src="
${
item
.
img
}
" />
<span class="flex-1">
<span class="md_link_url">
${
item
.
url
}
</span>
</span>
</span>
`
;
linkEl
.
innerHTML
=
renderLinkCard
(
title
,
item
);
});
// return vDom.innerHTML;
this
.
html
=
vDom
.
innerHTML
;
...
...
src/assets/js/utils.js
浏览文件 @
61d8025d
...
...
@@ -315,11 +315,24 @@ export function addLanguageClass(html) {
});
return
virtualDom
;
}
export
function
addLinkTarget
(
html
)
{
const
virtualDom
=
document
.
createElement
(
"
div
"
);
virtualDom
.
innerHTML
=
html
;
const
userList
=
[];
Array
.
from
(
virtualDom
.
querySelectorAll
(
"
a[href]
"
)).
forEach
(
item
=>
{
item
.
setAttribute
(
"
target
"
,
"
_blank
"
);
if
(
item
.
getAttribute
(
"
type
"
)
===
"
user
"
)
{
userList
.
push
(
item
.
dataset
.
user
);
}
});
const
list
=
Array
.
from
(
new
Set
(
userList
));
// 去重
return
{
callUserList
:
list
,
userHtml
:
virtualDom
.
innerHTML
};
}
export
function
getLinkTags
(
id
,
html
)
{
const
virtualDom
=
document
.
createElement
(
"
div
"
);
virtualDom
.
innerHTML
=
html
;
const
links
=
Array
.
from
(
virtualDom
.
querySelectorAll
(
"
a:not([download])
"
)
virtualDom
.
querySelectorAll
(
"
a:not([download]
,[type=user]
)
"
)
).
map
((
item
,
index
)
=>
{
item
.
id
=
id
+
"
_
"
+
index
;
return
{
...
...
@@ -336,6 +349,22 @@ export function getLinkTitle(linkEl) {
return
/^http/
.
test
(
title
)
?
""
:
title
;
}
export
function
renderLinkCard
(
title
,
item
)
{
return
`
<span class="md_link_title">
${
title
||
item
.
title
||
""
}
</span>
${
item
.
desc
?
`<span class="md_link_desc">
${
item
.
desc
}
</span>`
:
""
}
<span class="md_flex_card">
${
item
.
img
?
`<img class="md_link_img" src="
${
item
.
img
}
" />`
:
"
<span class='md_link_img icon iconfont icon-lianjie'></span>
"
}
<span class="flex-1">
<span class="md_link_url">
${
item
.
url
}
</span>
</span>
</span>`
;
}
export
function
preventDefault
(
id
)
{
const
textEl
=
document
.
getElementById
(
id
);
textEl
.
blur
();
...
...
src/assets/style/card.less
浏览文件 @
61d8025d
.md_link_card {
display: block;
padding:
10
px 16px;
padding:
6
px 16px;
box-sizing: border-box;
border-radius: 4px;
border: 1px solid var(--md-editor-border-color);
background: #f5f7fa;
transition: border 0.3s;
max-width: 640px;
margin: 4px 0;
// max-width: 640px;
@media (any-hover: hover) {
&:hover {
border: 1px solid var(--md-editor-border-color-active);
...
...
@@ -16,8 +17,9 @@
color: var(--md-editor-text-color-active);
}
.md_link_img {
height: 20px;
margin-right: 12px;
height: 14px !important;
line-height: 14px !important;
margin-right: 10px;
border-radius: 2px;
}
.md_link_title {
...
...
src/assets/style/global.less
浏览文件 @
61d8025d
...
...
@@ -65,7 +65,13 @@ textarea {
}
}
.md_call_user {
a
.md_call_user {
color: var(--md-editor-border-color-active);
cursor: pointer;
@media (any-hover: hover) {
&:hover {
color: var(--md-editor-border-color-active);
opacity: 0.8;
}
}
}
src/components/content/components/user-select.vue
浏览文件 @
61d8025d
...
...
@@ -13,7 +13,7 @@
>
<img
class=
"md_select_user_avatar"
:src=
"item.avatar"
/>
<div
class=
"md_select_user_info"
>
<div
class=
"md_select_user_name"
>
{{
item
.
name
}}
</div>
<div
class=
"md_select_user_name"
>
{{
item
.
n
ickn
ame
}}
</div>
<div
class=
"md_select_user_desc"
>
最近回答过类似问题
</div>
</div>
</li>
...
...
@@ -37,8 +37,8 @@ export default {
}
},
userList
:
{
type
:
Array
,
default
:
()
=>
[]
type
:
[
Array
,
Boolean
]
,
default
:
false
},
activeUserIndex
:
{
type
:
Number
,
...
...
src/components/content/md-textarea.vue
浏览文件 @
61d8025d
...
...
@@ -128,8 +128,8 @@ export default {
default
:
false
},
userList
:
{
type
:
Array
,
default
:
()
=>
[]
type
:
[
Boolean
,
Array
]
,
default
:
false
}
},
...
...
src/components/content/mixins/render-mixins.js
浏览文件 @
61d8025d
import
{
getFilteredTags
,
getLinkTags
,
addLanguageClass
addLanguageClass
,
addLinkTarget
}
from
"
@/assets/js/utils
"
;
import
marked
from
"
marked
"
;
import
DOMPurify
from
"
dompurify
"
;
...
...
@@ -36,12 +37,14 @@ export default {
const
filteredTags
=
getFilteredTags
(
html
,
cleanHtml
);
// 计算是否有标签被过滤
// 链接转换为卡片
const
{
vDom
,
links
}
=
getLinkTags
(
this
.
id
,
cleanHtml
);
const
{
callUserList
,
userHtml
}
=
addLinkTarget
(
cleanHtml
);
this
.
$emit
(
"
callUserList
"
,
callUserList
);
this
.
$emit
(
"
getFilteredTags
"
,
filteredTags
);
this
.
$emit
(
"
update:html
"
,
clean
Html
);
this
.
$emit
(
"
update:html
"
,
user
Html
);
if
(
links
.
length
)
this
.
$emit
(
"
renderLinksHtml
"
,
{
vDom
,
links
});
},
rerender
()
{
const
_this
=
this
;
const
renderer
=
{
image
(
href
,
title
,
text
)
{
if
(
href
===
null
)
{
...
...
@@ -56,7 +59,7 @@ export default {
<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>
<a href="
${
href
}
"
type="file" download
class="md_file_download icon iconfont icon-xiazai"></a>
</span>
</div>`
;
}
...
...
@@ -69,8 +72,11 @@ export default {
return
out
;
},
text
(
text
)
{
const
newText
=
text
.
replace
(
/
(
@.+
?
)
/g
,
`<span class="md_call_user">$1</span>`
);
const
newText
=
text
.
replace
(
/
(
@.+
?
)
/g
,
function
(
val
)
{
const
user
=
_this
.
getUserByName
(
val
.
slice
(
1
).
trim
());
return
`<a type="user" data-user="
${
user
&&
user
.
username
}
" href="
${
user
&&
user
.
url
}
" class="md_call_user">
${
val
}
</a>`
;
});
return
newText
;
}
};
...
...
src/components/content/mixins/select-user-mixins.js
浏览文件 @
61d8025d
...
...
@@ -5,7 +5,7 @@ export default {
const
originalText
=
this
.
textContent
;
const
queryInfo
=
this
.
queryInfo
;
const
cursorPosition
=
getPosition
(
this
.
id
);
const
username
=
user
.
name
+
"
"
;
const
username
=
user
.
n
ickn
ame
+
"
"
;
const
newText
=
originalText
.
slice
(
0
,
queryInfo
.
startPosition
)
+
username
+
...
...
@@ -79,20 +79,29 @@ export default {
pEl
.
offsetLeft
<
frameWidth
*
(
2
/
3
)
?
pEl
.
offsetLeft
:
pEl
.
offsetLeft
-
200
,
top
:
pEl
.
offsetTop
-
textEl
.
scrollTop
to
z
p
:
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
);
this
.
$nextTick
(()
=>
{
const
list
=
textEl
.
parentNode
.
querySelector
(
"
.md_select_user
"
);
if
(
list
)
list
.
scrollTo
(
0
,
0
);
const
userList
=
this
.
userList
;
if
(
userList
===
false
)
return
;
this
.
showSelectUser
=
true
;
this
.
$nextTick
(()
=>
{
const
list
=
textEl
.
parentNode
.
querySelector
(
"
.md_select_user
"
);
if
(
list
)
list
.
scrollTo
(
0
,
0
);
});
});
});
},
getUserByName
(
name
)
{
const
userList
=
this
.
userList
;
if
(
!
userList
.
length
)
return
""
;
return
userList
.
find
(
item
=>
item
.
nickname
===
name
);
}
}
};
src/components/header/md-header.vue
浏览文件 @
61d8025d
...
...
@@ -230,13 +230,13 @@ export default {
startStr
:
""
,
endStr
:
""
},
{
name
:
"
file
"
,
icon
:
"
wenjian
"
,
tip
:
"
上传附件
"
,
startStr
:
""
,
endStr
:
""
},
//
{
//
name: "file",
//
icon: "wenjian",
//
tip: "上传附件",
//
startStr: "",
//
endStr: ""
//
},
{
name
:
"
task
"
,
icon
:
"
renwu
"
,
...
...
src/main.js
浏览文件 @
61d8025d
...
...
@@ -22,8 +22,8 @@ function initMdEditor(obj) {
onBlur
=
()
=>
{},
onInput
=
()
=>
{},
onSubmit
=
()
=>
{},
renderLinks
=
()
=>
{}
,
queryUserList
=
()
=>
{}
,
renderLinks
,
queryUserList
,
placeholder
,
value
,
disabled
,
...
...
@@ -116,71 +116,72 @@ function initMdEditor(obj) {
});
},
renderLinks
({
links
,
callback
})
{
if
(
!
renderLinks
)
return
callback
(
links
);
renderLinks
(
links
,
function
(
res
)
{
callback
(
res
);
});
},
queryUserList
({
keyWord
,
callback
})
{
// queryUserList(keyWord, function(res) {
const
list
=
[
{
id
:
1
,
name
:
"
藤原拓海
"
,
avatar
:
"
https://img2.baidu.com/it/u=2380211986,3979961921&fm=26&fmt=auto&gp=0.jpg
"
},
{
id
:
2
,
name
:
"
高桥凉介
"
,
avatar
:
"
https://img0.baidu.com/it/u=777620324,2343967729&fm=26&fmt=auto&gp=0.jpg
"
},
{
id
:
3
,
name
:
"
马奎斯
"
,
avatar
:
"
https://img2.baidu.com/it/u=1297316011,1869565258&fm=26&fmt=auto&gp=0.jpg
"
},
{
id
:
4
,
name
:
"
王一博
"
,
url
:
"
https://weibo.com/u/5492443184
"
,
avatar
:
"
https://img2.baidu.com/it/u=298051053,3773223854&fm=26&fmt=auto&gp=0.jpg
"
},
{
id
:
5
,
name
:
"
王俊凯
"
,
url
:
"
https://weibo.com/tfwangjunkai
"
,
avatar
:
"
https://img1.baidu.com/it/u=2378425879,2273515018&fm=26&fmt=auto&gp=0.jpg
"
},
{
id
:
6
,
name
:
"
易烊千玺
"
,
url
:
"
https://weibo.com/tfyiyangqianxi
"
,
avatar
:
"
https://img0.baidu.com/it/u=2227200088,1939721201&fm=26&fmt=auto&gp=0.jpg
"
},
{
id
:
7
,
name
:
"
白敬亭
"
,
url
:
"
https://weibo.com/u/2112496475
"
,
avatar
:
"
https://img1.baidu.com/it/u=3265411836,2089649447&fm=11&fmt=auto&gp=0.jpg
"
}
];
if
(
!
queryUserList
)
return
callback
(
false
);
// 返回false则不触发@弹窗
queryUserList
(
keyWord
,
function
(
res
)
{
// const list = [
// {
// id: 1,
// name: "藤原拓海",
// avatar:
// "https://img2.baidu.com/it/u=2380211986,3979961921&fm=26&fmt=auto&gp=0.jpg"
// },
// {
// id: 2,
// name: "高桥凉介",
// avatar:
// "https://img0.baidu.com/it/u=777620324,2343967729&fm=26&fmt=auto&gp=0.jpg"
// },
// {
// id: 3,
// name: "马奎斯",
// avatar:
// "https://img2.baidu.com/it/u=1297316011,1869565258&fm=26&fmt=auto&gp=0.jpg"
// },
// {
// id: 4,
// name: "王一博",
// url: "https://weibo.com/u/5492443184",
// avatar:
// "https://img2.baidu.com/it/u=298051053,3773223854&fm=26&fmt=auto&gp=0.jpg"
// },
// {
// id: 5,
// name: "王俊凯",
// url: "https://weibo.com/tfwangjunkai",
// avatar:
// "https://img1.baidu.com/it/u=2378425879,2273515018&fm=26&fmt=auto&gp=0.jpg"
// },
// {
// id: 6,
// name: "易烊千玺",
// url: "https://weibo.com/tfyiyangqianxi",
// avatar:
// "https://img0.baidu.com/it/u=2227200088,1939721201&fm=26&fmt=auto&gp=0.jpg"
// },
// {
// id: 7,
// name: "白敬亭",
// url: "https://weibo.com/u/2112496475",
// avatar:
// "https://img1.baidu.com/it/u=3265411836,2089649447&fm=11&fmt=auto&gp=0.jpg"
// }
// ];
// callback(res);
if
(
!
keyWord
)
{
callback
(
list
);
return
;
}
callback
(
list
.
filter
(
item
=>
{
return
item
.
name
.
includes
(
keyWord
);
})
);
// });
const
list
=
res
;
if
(
!
keyWord
)
return
callback
(
list
);
callback
(
list
.
filter
(
item
=>
{
return
item
.
nickname
.
includes
(
keyWord
);
})
);
});
}
};
this
.
vEl
=
new
Vue
({
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录