Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
璃白.
markdown-editor
提交
c97c8733
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,发现更多精彩内容 >>
提交
c97c8733
编写于
6月 03, 2021
作者:
璃白.
🌻
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
feat:添加功能键
上级
c2be8bbe
变更
16
隐藏空白更改
内联
并排
Showing
16 changed file
with
396 addition
and
128 deletion
+396
-128
package.json
package.json
+3
-2
src/App.vue
src/App.vue
+19
-39
src/assets/js/utils.js
src/assets/js/utils.js
+21
-0
src/assets/style/font/iconfont.ttf
src/assets/style/font/iconfont.ttf
+0
-0
src/assets/style/iconfont.less
src/assets/style/iconfont.less
+5
-1
src/assets/style/variable.less
src/assets/style/variable.less
+2
-0
src/components/content/md-preview.vue
src/components/content/md-preview.vue
+42
-0
src/components/content/md-textarea.vue
src/components/content/md-textarea.vue
+98
-0
src/components/footer/md-footer.vue
src/components/footer/md-footer.vue
+10
-2
src/components/footer/upload-files.vue
src/components/footer/upload-files.vue
+3
-3
src/components/header/md-header.vue
src/components/header/md-header.vue
+15
-52
src/components/header/tool-button.vue
src/components/header/tool-button.vue
+84
-0
src/components/tool-button.vue
src/components/tool-button.vue
+0
-27
src/main.js
src/main.js
+11
-1
src/store/index.js
src/store/index.js
+82
-0
webpack.config.js
webpack.config.js
+1
-1
未找到文件。
package.json
浏览文件 @
c97c8733
{
"scripts"
:
{
"dev"
:
"webpack serve --mode=development"
,
"build"
:
"webpack"
"build"
:
"webpack
--mode=production
"
},
"devDependencies"
:
{
"
css-loader
"
:
"
^5.2.6
"
,
...
...
@@ -17,6 +17,7 @@
},
"dependencies"
:
{
"
marked
"
:
"
^2.0.6
"
,
"
vue
"
:
"
^2.6.12
"
"
vue
"
:
"
^2.6.12
"
,
"
vuex
"
:
"
^3.6.2
"
}
}
src/App.vue
浏览文件 @
c97c8733
<
template
>
<div
class=
"md_container"
>
<markdown-header
:showPreview.sync=
"showPreview"
/>
<markdownPreview
v-if=
"showPreview"
:content=
"html"
/>
<textarea
v-else
@
change=
"changeText"
@
focus=
"isFocus = true"
@
blur=
"isFocus = false"
v-model=
"text"
rows=
"10"
></textarea>
<div
:class=
"['md_container',
{ active: isFocus }]">
<markdown-header
/>
<markdownPreview
v-if=
"showPreview"
/>
<markdown-editor
v-else
/>
<markdown-footer
v-if=
"!showPreview"
/>
</div>
</
template
>
<
script
>
import
markdownHeader
from
"
./components/md-header
"
;
import
markdownFooter
from
"
./components/md-footer
"
;
import
markdownPreview
from
"
./components/md-preview
"
;
import
marked
from
"
marked
"
;
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
{
mapState
}
from
"
vuex
"
;
export
default
{
components
:
{
markdownHeader
,
markdownFooter
,
markdownPreview
},
data
()
{
return
{
text
:
""
,
html
:
""
,
showPreview
:
false
,
isFocus
:
false
};
components
:
{
markdownHeader
,
markdownFooter
,
markdownEditor
,
markdownPreview
},
methods
:
{
changeText
()
{
const
text
=
this
.
text
;
this
.
html
=
marked
(
text
);
this
.
$emit
(
"
change
"
,
{
text
,
html
:
this
.
html
});
}
computed
:
{
...
mapState
([
"
showPreview
"
,
"
isFocus
"
])
}
};
</
script
>
...
...
@@ -46,15 +32,9 @@ export default {
border-radius: 4px;
padding: 10px 16px;
box-sizing: border-box;
textarea {
display: block;
width: 100%;
padding: 10px 0;
box-sizing: border-box;
color: #303030;
resize: none;
font-family: "Menlo", "DejaVu Sans Mono", "Liberation Mono", "Consolas",
"Ubuntu Mono", "Courier New", "andale mono", "lucida console", monospace;
transition: border 0.3s;
&.active {
border: 1px solid var(--md-editor-theme-color-active);
}
}
</
style
>
src/assets/js/utils.js
0 → 100644
浏览文件 @
c97c8733
export
function
getSelectionInfo
(
selectorId
)
{
const
selector
=
document
.
getElementById
(
selectorId
);
// const selection = window.getSelection();
const
{
selectionStart
=
0
,
selectionEnd
=
0
}
=
selector
;
if
(
selectionStart
===
selectionEnd
)
return
""
;
return
{
selectionStart
,
selectionEnd
};
}
export
function
formatText
(
text
,
selectionInfo
,
startStr
=
""
,
endStr
=
""
)
{
if
(
!
selectionInfo
)
return
text
+
startStr
+
endStr
;
return
(
text
.
slice
(
0
,
selectionInfo
.
selectionStart
)
+
startStr
+
text
.
slice
(
selectionInfo
.
selectionStart
,
selectionInfo
.
selectionEnd
)
+
endStr
+
text
.
slice
(
selectionInfo
.
selectionEnd
)
);
}
src/assets/style/iconfont.ttf
→
src/assets/style/
font/
iconfont.ttf
浏览文件 @
c97c8733
无法预览此类型文件
src/assets/style/iconfont.less
浏览文件 @
c97c8733
@font-face {
font-family: "iconfont"; /* Project id */
src: url('
iconfont.ttf?t=1622541171105
') format('truetype');
src: url('
./font/iconfont.ttf?t=1622632202276
') format('truetype');
}
.iconfont {
...
...
@@ -51,6 +51,10 @@
content: "\eaef";
}
.icon-quxiaoquanping_o:before {
content: "\eb98";
}
.icon-tupian:before {
content: "\e607";
}
...
...
src/assets/style/variable.less
浏览文件 @
c97c8733
:root {
--md-editor-theme-color: #dbdbdb;
--md-editor-theme-color-active: #409eff;
--md-editor-text-color: #303030;
--md-editor-text-color-active: #000;
}
src/components/md-preview.vue
→
src/components/
content/
md-preview.vue
浏览文件 @
c97c8733
<
template
>
<div
class=
"md_preview"
>
<div
v-html=
"
content
"
></div>
<div
v-html=
"
html
"
></div>
</div>
</
template
>
<
script
>
import
{
mapState
,
mapMutations
}
from
"
vuex
"
;
import
marked
from
"
marked
"
;
export
default
{
props
:
{
content
:
{
type
:
String
,
default
:
""
}
},
data
()
{
return
{};
},
computed
:
{
...
mapState
([
"
text
"
,
"
html
"
])
},
methods
:
{
...
mapMutations
([
"
setHtml
"
]),
transferMarkdown
(
val
)
{
if
(
!
val
.
trim
())
return
;
const
html
=
marked
(
val
);
this
.
setHtml
(
html
);
}
},
watch
:
{
text
:
{
immediate
:
true
,
handler
:
function
(
val
)
{
this
.
transferMarkdown
(
val
);
}
}
}
};
</
script
>
...
...
@@ -21,6 +36,7 @@ export default {
min-height: 170px;
padding: 10px 0;
box-sizing: border-box;
color: #303030;
color: var(--md-editor-text-color);
word-break: break-all;
}
</
style
>
src/components/content/md-textarea.vue
0 → 100644
浏览文件 @
c97c8733
<
template
>
<div
:class=
"['md_textarea',
{ fullScreen }]">
<textarea
:id=
"id"
@
change=
"setText(textContent)"
@
focus=
"setFocus(true)"
@
blur=
"setFocus(false)"
v-model=
"textContent"
rows=
"10"
>
</textarea>
<span
@
click=
"setFullScreen(false)"
v-if=
"fullScreen"
class=
"icon iconfont icon-quxiaoquanping_o"
></span>
</div>
</
template
>
<
script
>
import
{
mapState
,
mapMutations
}
from
"
vuex
"
;
import
{
getSelectionInfo
}
from
"
@/assets/js/utils
"
;
export
default
{
data
()
{
return
{
id
:
new
Date
().
getTime
(),
isFocus
:
false
,
textContent
:
""
};
},
created
()
{
document
.
addEventListener
(
"
mouseup
"
,
this
.
checkSelection
);
},
watch
:
{
text
:
{
immediate
:
true
,
handler
:
function
(
val
)
{
this
.
textContent
=
val
;
}
}
},
beforeDestroy
()
{
document
.
removeEventListener
(
"
mouseup
"
,
this
.
checkSelection
);
},
computed
:
{
...
mapState
([
"
text
"
,
"
fullScreen
"
])
},
methods
:
{
...
mapMutations
([
"
setText
"
,
"
setFullScreen
"
,
"
setSelectionInfo
"
,
"
setFocus
"
]),
checkSelection
()
{
const
info
=
getSelectionInfo
(
this
.
id
);
if
(
!
info
)
return
;
this
.
setSelectionInfo
(
info
);
}
}
};
</
script
>
<
style
lang=
"less"
scoped
>
.md_textarea {
position: relative;
padding: 10px 0;
&.fullScreen {
position: fixed;
width: 100vw;
height: 100vh;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 99;
padding: 40px 60px;
box-sizing: border-box;
textarea {
font-size: 20px;
}
}
textarea {
display: block;
width: 100%;
height: 100%;
box-sizing: border-box;
color: var(--md-editor-text-color);
resize: none;
font-family: "Menlo", "DejaVu Sans Mono", "Liberation Mono", "Consolas",
"Ubuntu Mono", "Courier New", "andale mono", "lucida console", monospace;
}
.icon {
position: absolute;
top: 20px;
right: 20px;
font-size: 32px;
cursor: pointer;
}
}
</
style
>
src/components/md-footer.vue
→
src/components/
footer/
md-footer.vue
浏览文件 @
c97c8733
<
template
>
<div
class=
"md_footer
"
>
<div
:class=
"['md_footer',
{ active: isFocus }]
">
<div
class=
"doc"
></div>
<upload-files
/>
</div>
</
template
>
<
script
>
import
uploadFiles
from
"
./upload-files
"
;
import
{
mapState
}
from
"
vuex
"
;
export
default
{
components
:
{
uploadFiles
},
data
()
{
return
{};
},
computed
:
{
...
mapState
([
"
isFocus
"
])
}
};
</
script
>
...
...
@@ -22,6 +26,10 @@ export default {
padding-top: 10px;
box-sizing: border-box;
font-size: 14px;
color: #666;
color: var(--md-editor-text-color);
transition: border-top 0.3s;
&.active {
border-top: 1px solid var(--md-editor-theme-color-active);
}
}
</
style
>
src/components/upload-files.vue
→
src/components/
footer/
upload-files.vue
浏览文件 @
c97c8733
<
template
>
<div
class=
"upload_files"
@
click=
"upload"
>
<span
:class=
"['icon iconfont', 'icon-tupian']
"
></span>
<span
class=
"icon iconfont icon-tupian
"
></span>
<span>
添加附件
</span>
</div>
</
template
>
...
...
@@ -19,12 +19,12 @@ export default {
cursor: pointer;
.icon {
font-size: 20px;
color:
#666
;
color:
var(--md-editor-text-color)
;
cursor: pointer;
display: inline-block;
vertical-align: bottom;
&:hover {
color:
#000
;
color:
var(--md-editor-text-color-active)
;
}
}
}
...
...
src/components/md-header.vue
→
src/components/
header/
md-header.vue
浏览文件 @
c97c8733
<
template
>
<div
class=
"md_header
"
>
<div
:class=
"['md_header',
{ active: isFocus }]
">
<div
class=
"header_tabs"
>
<div
:class=
"['tab_item',
{ active: !showPreview }]"
@click="
toggleTab('edit'
)"
@click="
setShowPreview(false
)"
>
编辑
</div>
<div
:class=
"['tab_item',
{ active: showPreview }]"
@click="
toggleTab('preview'
)"
@click="
setShowPreview(true
)"
>
预览
</div>
...
...
@@ -25,55 +25,14 @@
</
template
>
<
script
>
import
toolButton
from
"
./tool-button
"
;
import
{
mapState
,
mapMutations
}
from
"
vuex
"
;
export
default
{
components
:
{
toolButton
},
props
:
{
showPreview
:
{
type
:
Boolean
,
default
:
false
}
},
data
()
{
return
{
toolButtonList
:
[
{
name
:
"
bold
"
,
format
:
"
**
"
},
{
name
:
"
italic
"
},
{
name
:
"
baojiaquotation
"
},
{
name
:
"
code
"
},
{
name
:
"
lianjie
"
},
{
name
:
"
unorderedList
"
},
{
name
:
"
youxuliebiao
"
},
{
name
:
"
renwu
"
},
{
name
:
"
biaoge
"
},
{
name
:
"
fullScreen
"
}
]
};
computed
:
{
...
mapState
([
"
toolButtonList
"
,
"
isFocus
"
,
"
showPreview
"
])
},
methods
:
{
toggleTab
(
type
)
{
this
.
$emit
(
"
update:showPreview
"
,
type
===
"
preview
"
);
}
...
mapMutations
([
"
setShowPreview
"
])
}
};
</
script
>
...
...
@@ -83,7 +42,11 @@ export default {
justify-content: space-between;
align-items: center;
height: 32px;
transition: border-bottom 0.3s;
border-bottom: 1px solid var(--md-editor-theme-color);
&.active {
border-bottom: 1px solid var(--md-editor-theme-color-active);
}
.header_tabs {
display: flex;
justify-content: space-between;
...
...
@@ -91,7 +54,7 @@ export default {
padding-bottom: 10px;
box-sizing: border-box;
.tab_item {
color:
#666
;
color:
var(--md-editor-text-color)
;
cursor: pointer;
position: relative;
padding: 0 6px;
...
...
@@ -110,14 +73,14 @@ export default {
}
&:hover {
color:
#000
;
color:
var(--md-editor-text-color-active)
;
&::after {
width: 100%;
background:
#666
;
background:
var(--md-editor-theme-color)
;
}
}
&.active {
color:
#000
;
color:
var(--md-editor-text-color-active)
;
font-weight: 700;
&::after {
width: 100%;
...
...
src/components/header/tool-button.vue
0 → 100644
浏览文件 @
c97c8733
<
template
>
<div
@
click=
"handleTool(info.name)"
class=
"tool_button"
>
<span
:class=
"['icon iconfont', `icon-$
{info.icon}`]">
</span>
</div>
</
template
>
<
script
>
import
{
mapMutations
,
mapState
}
from
"
vuex
"
;
import
{
formatText
}
from
"
@/assets/js/utils
"
;
export
default
{
props
:
{
info
:
{
type
:
Object
,
default
:
()
=>
{}
}
},
computed
:
{
...
mapState
([
"
selectionInfo
"
,
"
text
"
,
"
ulNum
"
])
},
methods
:
{
...
mapMutations
([
"
setFullScreen
"
,
"
setText
"
,
"
setUlNum
"
]),
handleTool
(
type
)
{
switch
(
type
)
{
case
"
bold
"
:
this
.
updateText
(
"
**
"
,
"
**
"
);
break
;
case
"
italic
"
:
this
.
updateText
(
"
_
"
,
"
_
"
);
break
;
case
"
quote
"
:
this
.
updateText
(
"
\n
>
"
,
""
);
break
;
case
"
code
"
:
this
.
updateText
(
"
`
"
,
"
`
"
);
break
;
case
"
link
"
:
this
.
updateText
(
"
[
"
,
"
](url)
"
);
break
;
case
"
ul
"
:
this
.
updateText
(
"
\n
-
"
,
""
);
break
;
case
"
ol
"
:
let
ulNum
=
this
.
ulNum
;
this
.
updateText
(
`\n
${
ulNum
++
}
. `
,
""
);
this
.
setUlNum
(
ulNum
);
break
;
case
"
task
"
:
this
.
updateText
(
"
\n
- [ ]
"
,
""
);
break
;
case
"
table
"
:
this
.
updateText
(
`\n\n| header | header |\n| ------ | ------ |\n| cell | cell |\n| cell | cell |\n\n`
,
""
);
break
;
case
"
fullScreen
"
:
this
.
setFullScreen
(
true
);
break
;
default
:
break
;
}
},
updateText
(
startStr
,
endStr
)
{
const
selectionInfo
=
this
.
selectionInfo
;
const
originalText
=
this
.
text
;
const
newText
=
formatText
(
originalText
,
selectionInfo
,
startStr
,
endStr
);
if
(
!
newText
)
return
;
this
.
setText
(
newText
);
}
}
};
</
script
>
<
style
lang=
"less"
scoped
>
.tool_button {
.icon {
font-size: 18px;
color: var(--md-editor-text-color);
cursor: pointer;
&:hover {
color: var(--md-editor-text-color-active);
}
}
}
</
style
>
src/components/tool-button.vue
已删除
100644 → 0
浏览文件 @
c2be8bbe
<
template
>
<div
class=
"tool_button"
>
<span
:class=
"['icon iconfont', `icon-$
{info.name}`]">
</span>
</div>
</
template
>
<
script
>
export
default
{
props
:
{
info
:
{
type
:
Object
,
default
:
()
=>
{}
}
}
};
</
script
>
<
style
lang=
"less"
scoped
>
.tool_button {
.icon {
font-size: 18px;
color: #666;
cursor: pointer;
&:hover {
color: #000;
}
}
}
</
style
>
src/main.js
浏览文件 @
c97c8733
import
Vue
from
"
vue
"
;
import
App
from
"
./App
"
;
import
store
from
"
./store
"
;
import
"
@/assets/style/global.less
"
;
function
initStyle
(
val
)
{
document
.
documentElement
.
style
.
setProperty
(
"
--md-editor-theme-color-active
"
,
val
);
}
function
MdEditor
(
obj
)
{
const
{
el
,
onChange
}
=
obj
;
const
{
el
,
onChange
,
themeActive
}
=
obj
;
if
(
!
el
||
!
document
.
querySelector
(
el
))
throw
new
Error
(
"
请指定容器
"
);
initStyle
(
themeActive
);
new
Vue
({
store
,
render
:
h
=>
h
(
App
,
{
on
:
{
...
...
src/store/index.js
0 → 100644
浏览文件 @
c97c8733
import
Vue
from
"
vue
"
;
import
Vuex
from
"
vuex
"
;
Vue
.
use
(
Vuex
);
export
default
new
Vuex
.
Store
({
state
:
{
fullScreen
:
false
,
isFocus
:
false
,
showPreview
:
false
,
toolButtonList
:
[
{
name
:
"
bold
"
,
icon
:
"
bold
"
,
format
:
"
**
"
},
{
name
:
"
italic
"
,
icon
:
"
italic
"
},
{
name
:
"
quote
"
,
icon
:
"
baojiaquotation
"
},
{
name
:
"
code
"
,
icon
:
"
code
"
},
{
name
:
"
link
"
,
icon
:
"
lianjie
"
},
{
name
:
"
ul
"
,
icon
:
"
unorderedList
"
},
{
name
:
"
ol
"
,
icon
:
"
youxuliebiao
"
},
{
name
:
"
task
"
,
icon
:
"
renwu
"
},
{
name
:
"
table
"
,
icon
:
"
biaoge
"
},
{
name
:
"
fullScreen
"
,
icon
:
"
fullScreen
"
}
],
ulNum
:
1
,
text
:
"
4564564564657567
\n
3456456456456
"
,
selectionInfo
:
""
,
html
:
""
},
mutations
:
{
setFullScreen
(
state
,
val
)
{
state
.
fullScreen
=
val
;
},
setShowPreview
(
state
,
val
)
{
state
.
showPreview
=
val
;
},
setFocus
(
state
,
val
)
{
state
.
isFocus
=
val
;
},
setText
(
state
,
val
)
{
state
.
text
=
val
;
},
setSelectionInfo
(
state
,
val
)
{
state
.
selectionInfo
=
val
;
},
setHtml
(
state
,
val
)
{
state
.
html
=
val
;
},
setUlNum
(
state
,
val
)
{
state
.
ulNum
=
val
;
}
}
});
webpack.config.js
浏览文件 @
c97c8733
...
...
@@ -28,7 +28,7 @@ module.exports = {
},
{
test
:
/
\.
ttf
$/
,
test
:
/
\.
(
ttf|woff|png
)
$/
,
loader
:
"
url-loader
"
}
]
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录