Demo:右键菜单新增复制到剪贴板功能

This commit is contained in:
街角小林 2024-07-02 10:30:54 +08:00
parent 1fc5b951a0
commit b7722987b1
5 changed files with 138 additions and 10 deletions

View File

@ -207,7 +207,7 @@ export const copyNodeTree = (
}
// 图片转成dataURL
export const imgToDataUrl = src => {
export const imgToDataUrl = (src, returnBlob = false) => {
return new Promise((resolve, reject) => {
const img = new Image()
// 跨域图片需要添加这个属性,否则画布被污染了无法导出图片
@ -220,7 +220,13 @@ export const imgToDataUrl = src => {
let ctx = canvas.getContext('2d')
// 图片绘制到canvas里
ctx.drawImage(img, 0, 0, img.width, img.height)
resolve(canvas.toDataURL())
if (returnBlob) {
canvas.toBlob(blob => {
resolve(blob)
})
} else {
resolve(canvas.toDataURL())
}
} catch (e) {
reject(e)
}

View File

@ -100,7 +100,15 @@ export default {
removeNote: 'Remove note',
removeCustomStyles: 'Remove custom styles',
removeAllNodeCustomStyles: 'Remove all node custom styles',
exportNodeToPng: 'Export node to png'
exportNodeToPng: 'Export node to png',
copyToClipboard: 'Copy to clipboard',
copyToSmm: 'SMM',
copyToJson: 'JSON',
copyToMarkdown: 'Markdown',
copyToTxt: 'Txt',
copyToPng: 'Png',
copySuccess: 'Copy success',
copyFail: 'Copy fail'
},
count: {
words: 'Words',

View File

@ -100,7 +100,15 @@ export default {
removeNote: '移除备注',
removeCustomStyles: '一键去除自定义样式',
removeAllNodeCustomStyles: '一键去除所有节点自定义样式',
exportNodeToPng: '导出该节点为图片'
exportNodeToPng: '导出该节点为图片',
copyToClipboard: '复制到剪贴板',
copyToSmm: 'SMM',
copyToJson: 'JSON',
copyToMarkdown: 'Markdown',
copyToTxt: 'Txt',
copyToPng: '图片',
copySuccess: '复制成功',
copyFail: '复制失败'
},
count: {
words: '字数',

View File

@ -109,6 +109,7 @@
</div>
<div class="item">
<span class="name">{{ $t('contextmenu.expandTo') }}</span>
<span class="el-icon-arrow-right"></span>
<div class="subItems listBox" :class="{ isDark: isDark }">
<div
class="item"
@ -137,13 +138,30 @@
$t('contextmenu.removeAllNodeCustomStyles')
}}</span>
</div>
<div class="item">
<span class="name">{{ $t('contextmenu.copyToClipboard') }}</span>
<span class="el-icon-arrow-right"></span>
<div class="subItems listBox" :class="{ isDark: isDark }">
<div
class="item"
v-for="item in copyList"
:key="item.value"
@click="copyToClipboard(item.value)"
>
{{ item.name }}
</div>
</div>
</div>
</template>
</div>
</template>
<script>
import { mapState, mapMutations } from 'vuex'
import { getTextFromHtml } from 'simple-mind-map/src/utils'
import { getTextFromHtml, imgToDataUrl } from 'simple-mind-map/src/utils'
import { transformToMarkdown } from 'simple-mind-map/src/parse/toMarkdown'
import { transformToTxt } from 'simple-mind-map/src/parse/toTxt'
import { setDataToClipboard, setImgToClipboard, copy } from '@/utils'
/**
* @Author: 王林
@ -166,7 +184,8 @@ export default {
type: '',
isMousedown: false,
mosuedownX: 0,
mosuedownY: 0
mosuedownY: 0,
enableCopyToClipboardApi: navigator.clipboard
}
},
computed: {
@ -184,6 +203,33 @@ export default {
this.$t('contextmenu.level6')
]
},
copyList() {
const list = [
{
name: this.$t('contextmenu.copyToSmm'),
value: 'smm'
},
{
name: this.$t('contextmenu.copyToJson'),
value: 'json'
},
{
name: this.$t('contextmenu.copyToMarkdown'),
value: 'md'
},
{
name: this.$t('contextmenu.copyToTxt'),
value: 'txt'
}
]
if (this.enableCopyToClipboardApi) {
list.push({
name: this.$t('contextmenu.copyToPng'),
value: 'png'
})
}
return list
},
insertNodeBtnDisabled() {
return !this.node || this.node.isRoot || this.node.isGeneralization
},
@ -355,6 +401,48 @@ export default {
break
}
this.hide()
},
//
async copyToClipboard(type) {
try {
this.hide()
let data
let str
switch (type) {
case 'smm':
case 'json':
data = this.mindMap.getData(true)
str = JSON.stringify(data)
break
case 'md':
data = this.mindMap.getData()
str = transformToMarkdown(data)
break
case 'txt':
data = this.mindMap.getData()
str = transformToTxt(data)
break
case 'png':
const png = await this.mindMap.export('png', false)
const blob = await imgToDataUrl(png, true)
setImgToClipboard(blob)
break
default:
break
}
if (str) {
if (this.enableCopyToClipboardApi) {
setDataToClipboard(str)
} else {
copy(str)
}
}
this.$message.success(this.$t('contextmenu.copySuccess'))
} catch (error) {
console.log(error)
this.$message.error(this.$t('contextmenu.copyFail'))
}
}
}
}
@ -393,11 +481,11 @@ export default {
.item {
position: relative;
height: 28px;
line-height: 28px;
padding: 0 16px;
cursor: pointer;
display: flex;
justify-content: space-between;
align-items: center;
&.danger {
color: #f56c6c;
@ -439,6 +527,7 @@ export default {
left: 100%;
top: 0;
visibility: hidden;
width: 150px;
}
}
}

View File

@ -49,11 +49,28 @@ export const fileToBuffer = file => {
}
// 复制文本到剪贴板
export const copy = (text) => {
const input = document.createElement('input')
input.setAttribute('value', text)
export const copy = text => {
// 使用textarea可以保留换行
const input = document.createElement('textarea')
// input.setAttribute('value', text)
input.innerHTML = text
document.body.appendChild(input)
input.select()
document.execCommand('copy')
document.body.removeChild(input)
}
// 复制文本到剪贴板
export const setDataToClipboard = data => {
if (navigator.clipboard) {
navigator.clipboard.writeText(data)
}
}
// 复制图片到剪贴板
export const setImgToClipboard = img => {
if (navigator.clipboard) {
const data = [new ClipboardItem({ ['image/png']: img })]
navigator.clipboard.write(data)
}
}