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

feat:添加格式刷

上级 96e37f77
无法预览此类型文件
...@@ -51,7 +51,7 @@ void main() ...@@ -51,7 +51,7 @@ void main()
{ {
if(i%j==0) if(i%j==0)
{ {
a+=i/j;{1} a+=i/j;
} }
} }
if(i==a){ if(i==a){
...@@ -81,7 +81,7 @@ void main() ...@@ -81,7 +81,7 @@ void main()
// aaa.remove() // aaa.remove()
// }) // })
// `, // `,
value: "", value: "abcabcabc123**中文的**字体",
themeOptions: { themeOptions: {
dark: false, dark: false,
borderColorActive: "#409eff", borderColorActive: "#409eff",
...@@ -90,11 +90,12 @@ void main() ...@@ -90,11 +90,12 @@ void main()
codeTheme: "atom-one-dark" codeTheme: "atom-one-dark"
}, },
toolsOptions: { toolsOptions: {
format: true,
bold: true, bold: true,
italic: true, italic: true,
quote: false, quote: true,
code: true, code: true,
link: false, link: true,
ul: true, ul: true,
ol: true, ol: true,
task: true, task: true,
...@@ -103,7 +104,7 @@ void main() ...@@ -103,7 +104,7 @@ void main()
fullScreen: true fullScreen: true
}, },
zIndex: 7000, zIndex: 7000,
// maxLength: 1000, maxLength: 20000,
showWordLimit: true, showWordLimit: true,
rows: "10", rows: "10",
// height: "600", // height: "600",
......
因为 它太大了无法显示 source diff 。你可以改为 查看blob
...@@ -69,7 +69,6 @@ ...@@ -69,7 +69,6 @@
:can-attach-file="canAttachFile" :can-attach-file="canAttachFile"
v-if="!showPreview && canAttachFile" v-if="!showPreview && canAttachFile"
/> --> /> -->
<tableSelect />
</div> </div>
</template> </template>
<script> <script>
...@@ -77,15 +76,13 @@ import markdownHeader from "./components/header/md-header"; ...@@ -77,15 +76,13 @@ import markdownHeader from "./components/header/md-header";
import markdownFooter from "./components/footer/md-footer"; import markdownFooter from "./components/footer/md-footer";
import markdownEditor from "./components/content/md-textarea"; import markdownEditor from "./components/content/md-textarea";
import markdownPreview from "./components/content/md-preview"; import markdownPreview from "./components/content/md-preview";
import tableSelect from "./components/header/components/table-select";
import { formatText, checktUrl } from "@/assets/js/utils"; import { formatText, checktUrl } from "@/assets/js/utils";
export default { export default {
components: { components: {
markdownHeader, markdownHeader,
markdownFooter, markdownFooter,
markdownEditor, markdownEditor,
markdownPreview, markdownPreview
tableSelect
}, },
props: { props: {
placeholder: { placeholder: {
...@@ -329,7 +326,7 @@ export default { ...@@ -329,7 +326,7 @@ export default {
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.md_container { .md_container {
width: 100%; // width: 100%;
background: var(--md-editor-frame-bg-color); background: var(--md-editor-frame-bg-color);
// margin: 200px auto; // margin: 200px auto;
border: 1px solid var(--md-editor-border-color); border: 1px solid var(--md-editor-border-color);
......
...@@ -3,11 +3,12 @@ ...@@ -3,11 +3,12 @@
export function getSelectionInfo(selectorId) { export function getSelectionInfo(selectorId) {
const selector = document.getElementById(selectorId); const selector = document.getElementById(selectorId);
if (!selector) return; if (!selector) return;
// const selection = window.getSelection();
const { selectionStart = 0, selectionEnd = 0 } = selector; const { selectionStart = 0, selectionEnd = 0 } = selector;
if (selectionStart === selectionEnd) return ""; if (selectionStart === selectionEnd) return "";
const selection = window.getSelection().toString();
return { return {
selectorId, selectorId,
selection,
selectionStart, selectionStart,
selectionEnd selectionEnd
}; };
...@@ -188,3 +189,110 @@ export function getFilteredTags(oldStr, newStr) { ...@@ -188,3 +189,110 @@ export function getFilteredTags(oldStr, newStr) {
const filteredTags = Array.from(virtualDom.getElementsByTagName("*")); const filteredTags = Array.from(virtualDom.getElementsByTagName("*"));
return filteredTags; return filteredTags;
} }
// 格式刷的规则
export function copyFormatRules(selection) {
if (!selection) return "";
const first1 = selection.slice(0, 1);
const first2 = selection.slice(0, 2);
const first3 = selection.slice(0, 3);
const first4 = selection.slice(0, 4);
const first5 = selection.slice(0, 5);
const first6 = selection.slice(0, 6);
const first7 = selection.slice(0, 7);
const end1 = selection.slice(-1);
const end2 = selection.slice(-2);
const end3 = selection.slice(-3);
let formatType = "";
console.log(first6);
switch (true) {
// 斜体
case first1 === "_" && end1 === "_":
formatType = {
startStr: "_",
endStr: "_"
};
break;
// 加粗
case first2 === "**" && end2 === "**":
formatType = {
startStr: "**",
endStr: "**"
};
break;
// 代码
case first3 === "```" && end3 === "```":
formatType = {
startStr: "\n```\n",
endStr: "\n\n```"
};
break;
// 引用
case first2 === "> ":
formatType = {
startStr: "\n> ",
endStr: ""
};
break;
// 待办
case first6 === "- [ ] ":
formatType = {
startStr: "\n- [ ] ",
endStr: ""
};
break;
// 无序列表
case first2 === "- ":
formatType = {
startStr: "\n- ",
endStr: ""
};
break;
// H6
case first7 === "###### ":
formatType = {
startStr: "\n###### ",
endStr: ""
};
break;
// H5
case first6 === "##### ":
formatType = {
startStr: "\n##### ",
endStr: ""
};
break;
// H4
case first5 === "#### ":
formatType = {
startStr: "\n#### ",
endStr: ""
};
break;
// H3
case first4 === "### ":
formatType = {
startStr: "\n### ",
endStr: ""
};
break;
// H2
case first3 === "## ":
formatType = {
startStr: "\n## ",
endStr: ""
};
break;
// H1
case first2 === "# ":
formatType = {
startStr: "\n# ",
endStr: ""
};
break;
default:
break;
}
return formatType;
}
...@@ -6,6 +6,8 @@ ...@@ -6,6 +6,8 @@
padding: 0; padding: 0;
margin: 0; margin: 0;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0); -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
font-family: -apple-system, SF UI Text, Arial, PingFang SC, Hiragino Sans GB,
Microsoft YaHei, WenQuanYi Micro Hei, sans-serif, SimHei, SimSun;
} }
html, html,
......
@font-face { @font-face {
font-family: "iconfont"; /* Project id */ font-family: "iconfont"; /* Project id */
src: url('font/iconfont.ttf?t=1624355824015') format('truetype'); src: url('./font/iconfont.ttf') format('truetype');
} }
.iconfont { .iconfont {
...@@ -59,3 +59,11 @@ ...@@ -59,3 +59,11 @@
content: "\eb98"; content: "\eb98";
} }
.icon-geshishua:before {
content: "\e6bb";
}
.icon-yinyong:before {
content: "\e697";
}
...@@ -297,9 +297,9 @@ export default { ...@@ -297,9 +297,9 @@ export default {
color: var(--md-editor-text-color-active); color: var(--md-editor-text-color-active);
height: var(--md-editor-height); height: var(--md-editor-height);
resize: none; resize: none;
font-family: "Menlo", -apple-system, SF UI Text, Arial, PingFang SC,
font-family: "Menlo", "DejaVu Sans Mono", "Liberation Mono", "Consolas", Hiragino Sans GB, Microsoft YaHei, WenQuanYi Micro Hei, sans-serif, SimHei,
"Ubuntu Mono", "Courier New", "andale mono", "lucida console", monospace; SimSun;
&::placeholder { &::placeholder {
color: var(--md-editor-text-color); color: var(--md-editor-text-color);
} }
......
...@@ -52,11 +52,27 @@ export default { ...@@ -52,11 +52,27 @@ export default {
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
ul { ul {
max-height: 160px; max-height: 162px;
overflow-y: auto; overflow-y: auto;
margin-top: 2px; margin-top: 2px;
margin-bottom: 6px;
border-top: 1px solid #e1e1e1;
padding-top: 4px;
box-sizing: border-box;
scrollbar-color: transparent transparent;
// scrollbar-track-color: transparent;
// -ms-scrollbar-track-color: transparent;
&::-webkit-scrollbar { &::-webkit-scrollbar {
display: none; width: 2px;
height: 2px;
}
&::-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 { li {
cursor: pointer; cursor: pointer;
......
<template> <template>
<div class="block_container" ref="block_container"> <div
class="block_container"
@mousemove.stop="getCursorPoint"
@mouseout="resetCursorPoint"
@click.stop="handleSelectTable"
ref="block_container"
>
<span v-show="lastItem.row && lastItem.col" class="select_num"
>{{ lastItem.row }}&nbsp;x&nbsp;{{ lastItem.col }}</span
>
<div <div
@mousemove.stop="getCursorPoint" ref="block"
:class="['block', { active: item.active }]" :class="['block', { active: item.active }]"
v-for="(item, index) in list" v-for="(item, index) in list"
:key="index" :key="index"
...@@ -13,23 +22,98 @@ import { throttle as throttleFn } from "@/assets/js/utils"; ...@@ -13,23 +22,98 @@ import { throttle as throttleFn } from "@/assets/js/utils";
export default { export default {
data() { data() {
return { return {
list: new Array(40).fill({ list: new Array(60).fill(0).map((item, index) => {
index: 0, return {
active: false index: index + 1,
}) active: false,
row: Math.floor(index / 10) + 1,
col: (index % 10) + 1
};
}),
mousePoint: {
x: 0,
y: 0
},
dialogPositon: {
x: 0,
y: 0
},
lastItem: {
row: 0,
col: 0
}
}; };
}, },
computed: {}, created() {
setTimeout(() => {
// this.dialogPositon = this.getDialogPosition();
this.createBlockPoint();
}, 0);
},
methods: { methods: {
getCursorPoint(e) { handleSelectTable() {
console.log(e.clientX, e.clientY); const { row, col } = this.lastItem;
const target = e.target; const str = `\n\n|${this.createStr(" 表头 |", col)}\n|${this.createStr(
" ------ |",
col
)}\n${this.createStr("|" + this.createStr(" 单元格 |", col) + "\n", row)}`;
console.log(str);
const { offsetLeft, offsetTop } = target; this.$emit("select", str);
if (e.clientX > offsetLeft) { },
target.className = "block active"; createStr(str, num) {
} let res = "";
// console.log(offsetLeft, offsetTop); new Array(num).fill(0).forEach(item => {
res += str;
});
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 = {
x: e.clientX,
y: e.clientY
};
this.setBlockActive();
},
resetCursorPoint() {
this.lastItem = {
row: 0,
col: 0
};
this.list.forEach(item => {
item.active = false;
});
},
setBlockActive() {
const mousePoint = this.mousePoint;
const list = this.list;
list.forEach(item => {
item.active = mousePoint.x > item.x && mousePoint.y > item.y;
});
const lastItem = list.filter(item => item.active).pop();
if (!lastItem) return;
this.lastItem = { row: lastItem.row, col: lastItem.col };
} }
} }
}; };
...@@ -42,8 +126,14 @@ export default { ...@@ -42,8 +126,14 @@ export default {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
justify-content: space-around; justify-content: space-around;
cursor: pointer;
margin-bottom: 4px;
.select_num {
position: absolute;
top: 8px;
right: 10px;
}
.block { .block {
cursor: pointer;
width: @block-size; width: @block-size;
height: @block-size; height: @block-size;
box-sizing: border-box; box-sizing: border-box;
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
v-else v-else
v-tip.bottom="options" v-tip.bottom="options"
@click="handleTool(info.name, info.startStr, info.endStr)" @click="handleTool(info.name, info.startStr, info.endStr)"
@dblclick="handleDbClick(info.name)"
class="tool_button" class="tool_button"
> >
<span :class="['icon iconfont', `icon-${info.icon}`]"></span> <span :class="['icon iconfont', `icon-${info.icon}`]"></span>
...@@ -33,10 +34,10 @@ ...@@ -33,10 +34,10 @@
</template> </template>
<script> <script>
import { formatText, checkBoswer } from "@/assets/js/utils"; import { formatText, checkBoswer } from "@/assets/js/utils";
import languageList from "./language-list"; import codeSelect from "./code-select";
import tableSelect from "./table-select"; import tableSelect from "./table-select";
export default { export default {
components: { languageList, tableSelect }, components: { codeSelect, tableSelect },
props: { props: {
info: { info: {
type: Object, type: Object,
...@@ -68,9 +69,7 @@ export default { ...@@ -68,9 +69,7 @@ export default {
} }
}, },
data() { data() {
return { return {};
// ulNum: 1
};
}, },
computed: { computed: {
darkMode() { darkMode() {
...@@ -90,12 +89,14 @@ export default { ...@@ -90,12 +89,14 @@ export default {
codeOptions() { codeOptions() {
return { return {
content: this.info.tip, content: this.info.tip,
customComponent: languageList, customComponent: codeSelect,
customClass: "codeSelectDialog",
width: 110,
customListeners: { customListeners: {
select: val => { select: val => {
console.log(val); this.closeTips();
const lang = val.toLowerCase().replace(/-/, ""); const lang = val.toLowerCase().replace(/-/, "");
this.handleTool("code", "\n```" + lang, "\n\n\n```"); this.handleTool("code", "\n```" + lang + "\n", "\n\n\n```");
} }
}, },
zIndex: parseInt(this.zIndex) + 1, zIndex: parseInt(this.zIndex) + 1,
...@@ -105,12 +106,13 @@ export default { ...@@ -105,12 +106,13 @@ export default {
tableOptions() { tableOptions() {
return { return {
content: this.info.tip, content: this.info.tip,
customClass: "tableSelectDialog",
customComponent: tableSelect, customComponent: tableSelect,
customListeners: { customListeners: {
select: val => { select: val => {
console.log(val); console.log(val);
const lang = val.toLowerCase().replace(/-/, ""); this.handleTool("table", val, "");
this.handleTool("code", "\n```" + lang, "\n\n\n```"); this.closeTips();
} }
}, },
zIndex: parseInt(this.zIndex) + 1, zIndex: parseInt(this.zIndex) + 1,
...@@ -119,6 +121,17 @@ export default { ...@@ -119,6 +121,17 @@ export default {
} }
}, },
methods: { methods: {
closeTips() {
Array.from(document.getElementsByClassName("v-tip-container")).map(
item => {
item.remove();
}
);
},
handleDbClick(type) {
if (type !== "format") return;
this.$emit("setFormatType", { lock: true });
},
handleTool(type, startStr, endStr) { handleTool(type, startStr, endStr) {
switch (type) { switch (type) {
case "bold": case "bold":
...@@ -129,11 +142,14 @@ export default { ...@@ -129,11 +142,14 @@ export default {
case "ul": case "ul":
case "task": case "task":
case "table": case "table":
this.updateText(startStr, endStr); this.$emit("updateText", { startStr, endStr });
break;
case "format":
this.$emit("setFormatType", { lock: false });
break; break;
case "ol": case "ol":
let ulNum = this.ulNum; let ulNum = this.ulNum;
this.updateText(`\n${ulNum++}. `, ""); this.$emit("updateText", { startStr: `\n${ulNum++}. `, endStr: "" });
this.$emit("update:ulNum", ulNum); this.$emit("update:ulNum", ulNum);
break; break;
case "file": case "file":
...@@ -148,17 +164,6 @@ export default { ...@@ -148,17 +164,6 @@ export default {
default: default:
break; break;
} }
},
updateText(startStr, endStr) {
const originalText = this.text;
const selectionInfo = this.selectionInfo;
const newText = formatText(originalText, selectionInfo, startStr, endStr);
const len =
selectionInfo.selectionEnd -
selectionInfo.selectionStart +
startStr.length;
this.$emit("updateText", { val: newText, len });
} }
} }
}; };
...@@ -168,14 +173,20 @@ export default { ...@@ -168,14 +173,20 @@ export default {
padding: 4px 8px; padding: 4px 8px;
box-sizing: border-box; box-sizing: border-box;
cursor: pointer; cursor: pointer;
&.active {
.icon {
color: var(--md-editor-border-color-active);
}
}
.icon { .icon {
font-size: 18px; font-size: 18px;
color: var(--md-editor-text-color); color: var(--md-editor-text-color);
@media (any-hover: hover) { @media (any-hover: hover) {
&:hover { &:hover {
color: var(--md-editor-text-color-active); color: var(--md-editor-border-color-active);
} }
} }
&.icon-quxiaoquanping_o { &.icon-quxiaoquanping_o {
font-size: 24px; font-size: 24px;
margin: 0 -4px; margin: 0 -4px;
......
...@@ -24,9 +24,12 @@ ...@@ -24,9 +24,12 @@
@setFullScreen="$emit('update:fullScreen', $event)" @setFullScreen="$emit('update:fullScreen', $event)"
@updateText="handleUpdateText" @updateText="handleUpdateText"
@upload="$emit('upload')" @upload="$emit('upload')"
@setFormatType="setFormatType"
:class="{ active: item.name === 'format' && formatType }"
v-for="(item, index) in toolsShow" v-for="(item, index) in toolsShow"
:key="index" :key="index"
:text="text" :text="text"
:formatType.sync="formatType"
:zIndex="zIndex" :zIndex="zIndex"
:themeOptions="themeOptions" :themeOptions="themeOptions"
:selectionInfo="selectionInfo" :selectionInfo="selectionInfo"
...@@ -39,7 +42,8 @@ import { ...@@ -39,7 +42,8 @@ import {
isNotFalse, isNotFalse,
formatText, formatText,
getPosition, getPosition,
removeBlankLine removeBlankLine,
copyFormatRules
} from "@/assets/js/utils"; } from "@/assets/js/utils";
import toolButton from "./components/tool-button"; import toolButton from "./components/tool-button";
export default { export default {
...@@ -113,8 +117,15 @@ export default { ...@@ -113,8 +117,15 @@ export default {
} }
} }
}, },
text: { selectionInfo: {
handler: function(newVal, oldVal) {} handler: function(newVal, oldVal) {
if (
newVal.selectionStart === oldVal.selectionStart &&
newVal.selectionEnd === oldVal.selectionEnd
)
return;
this.copyFormat();
}
} }
}, },
data() { data() {
...@@ -125,6 +136,8 @@ export default { ...@@ -125,6 +136,8 @@ export default {
icon: "quxiaoquanping_o", icon: "quxiaoquanping_o",
tip: "退出全屏" tip: "退出全屏"
}, },
formatType: "", // 格式刷类型
lock: false,
ulNum: 1, ulNum: 1,
fullScreenBtn: { fullScreenBtn: {
name: "fullScreen", name: "fullScreen",
...@@ -132,6 +145,11 @@ export default { ...@@ -132,6 +145,11 @@ export default {
tip: "全屏模式" tip: "全屏模式"
}, },
toolButtonList: [ toolButtonList: [
{
name: "format",
icon: "geshishua",
tip: "格式刷"
},
{ {
name: "bold", name: "bold",
icon: "bold", icon: "bold",
...@@ -148,7 +166,7 @@ export default { ...@@ -148,7 +166,7 @@ export default {
}, },
{ {
name: "quote", name: "quote",
icon: "baojiaquotation", icon: "yinyong",
tip: "插入引用", tip: "插入引用",
startStr: "\n> ", startStr: "\n> ",
endStr: "" endStr: ""
...@@ -157,15 +175,15 @@ export default { ...@@ -157,15 +175,15 @@ export default {
name: "code", name: "code",
icon: "code", icon: "code",
tip: "插入代码块", tip: "插入代码块",
startStr: "\n```", startStr: "\n```\n",
endStr: "\n\n\n```" endStr: "\n\n\n```"
}, },
{ {
name: "link", name: "link",
icon: "lianjie", icon: "lianjie",
tip: "添加链接", tip: "添加链接",
startStr: "[", startStr: "[链接](",
endStr: "](url)" endStr: ")"
}, },
{ {
name: "ul", name: "ul",
...@@ -200,7 +218,7 @@ export default { ...@@ -200,7 +218,7 @@ export default {
icon: "biaoge", icon: "biaoge",
tip: "添加表格", tip: "添加表格",
startStr: startStr:
"\n| header | header |\n| ------ | ------ |\n| cell | cell |\n| cell | cell |\n\n", "\n\n| 表头 | 表头 |\n| ------ | ------ |\n| 单元格 | 单元格 |\n| 单元格 | 单元格 |\n\n",
endStr: "" endStr: ""
}, },
{ {
...@@ -211,11 +229,36 @@ export default { ...@@ -211,11 +229,36 @@ export default {
] ]
}; };
}, },
methods: { methods: {
resetUlNum() { resetUlNum() {
this.ulNum = 1; this.ulNum = 1;
}, },
setFormatType({ lock }) {
if (this.formatType) {
this.formatType = "";
return;
}
const selectionInfo = this.selectionInfo;
const selection = this.text.slice(
selectionInfo.selectionStart,
selectionInfo.selectionEnd
);
const formatType = copyFormatRules(selection);
this.formatType = formatType;
this.lock = lock;
},
copyFormat() {
const selectionInfo = this.selectionInfo;
const formatType = this.formatType;
if (
selectionInfo.selectionStart === selectionInfo.selectionEnd ||
!formatType
)
return;
this.handleUpdateText(formatType);
if (this.lock) return;
this.formatType = "";
},
tab() { tab() {
const textEl = document.getElementById(this.id); const textEl = document.getElementById(this.id);
const scrollTop = textEl.scrollTop; const scrollTop = textEl.scrollTop;
...@@ -238,9 +281,16 @@ export default { ...@@ -238,9 +281,16 @@ export default {
setShowPreview(val) { setShowPreview(val) {
this.$emit("update:showPreview", val); this.$emit("update:showPreview", val);
}, },
handleUpdateText({ val, len }) {
// const cursorPoint = getPosition(this.id); handleUpdateText({ startStr, endStr }) {
this.updateText(val, len); const originalText = this.text;
const selectionInfo = this.selectionInfo;
const newText = formatText(originalText, selectionInfo, startStr, endStr);
const len =
selectionInfo.selectionEnd -
selectionInfo.selectionStart +
startStr.length;
this.updateText(newText, len);
}, },
updateText(val, len = 0) { updateText(val, len = 0) {
const textEl = document.getElementById(this.id); const textEl = document.getElementById(this.id);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册