feat: 添加多选功能 支持合并文本/图片/文件

This commit is contained in:
ZiuChen 2022-09-08 23:49:11 +08:00
parent 9b172f684c
commit cfe567b9fb
5 changed files with 170 additions and 28 deletions

View File

@ -15,7 +15,8 @@ const dbName = '_utools_clipboard_manager_storage'
const isMacOs = utools.isMacOs()
const isWindows = utools.isWindows()
const DBPath = `${isMacOs ? userDataPath : homePath}${isWindows ? '\\' : '/'}${dbName}`
const sep = isWindows ? '\\' : '/'
const DBPath = `${isMacOs ? userDataPath : homePath}${sep}${dbName}`
let globalImageOversize = false
@ -45,7 +46,7 @@ class DB {
)
this.updateDataBaseLocal()
} catch (err) {
utools.showNotification('读取剪切板出错' + err)
utools.showNotification('读取剪切板出错: ' + err)
return
}
return
@ -66,7 +67,7 @@ class DB {
// 更新文件数据
fs.writeFileSync(this.path, JSON.stringify(dataBase || this.dataBase), (err) => {
if (err) {
utools.showNotification('写入剪切板出错' + err)
utools.showNotification('写入剪切板出错: ' + err)
return
}
})
@ -186,6 +187,30 @@ const paste = () => {
else utools.simulateKeyboardTap('v', 'ctrl')
}
const createFile = (item) => {
const tempPath = utools.getPath('temp')
const folderPath = tempPath + sep + 'utools-clipboard-manager'
if (!fs.existsSync(folderPath)) {
try {
fs.mkdirSync(folderPath)
} catch (err) {
utools.showNotification('创建临时文件夹出错: ' + err)
}
}
const { type } = item
if (type === 'image') {
const base64Data = item.data.replace(/^data:image\/\w+;base64,/, '') // remove the prefix
const buffer = Buffer.from(base64Data, 'base64') // to Buffer
const filePath = folderPath + sep + new Date().valueOf() + '.png'
fs.writeFileSync(filePath, buffer)
return filePath
} else if (type === 'text') {
const filePath = folderPath + sep + new Date().valueOf() + '.txt'
fs.writeFileSync(filePath, item.data)
return filePath
}
}
const db = new DB(DBPath)
db.init()
@ -231,6 +256,7 @@ window.db = db
window.copy = copy
window.paste = paste
window.remove = remove
window.createFile = createFile
window.openFile = utools.shellOpenPath
window.openFileFolder = utools.shellShowItemInFolder
window.getIcon = utools.getFileIcon

View File

@ -7,7 +7,7 @@
@click.left="handleItemClick($event, item)"
@click.right="handleItemClick($event, item)"
@mouseover="handleMouseOver(index)"
:class="{ active: index === activeIndex }"
:class="{ active: index === activeIndex, select: selectItemList.indexOf(item) !== -1 }"
>
<div class="clip-info">
<div class="clip-time">
@ -29,7 +29,7 @@
</template>
</div>
</div>
<div class="clip-operate" v-show="activeIndex === index">
<div class="clip-operate" v-show="activeIndex === index && !isMultiple">
<template v-for="{ id, title, icon } of operation">
<div
v-if="
@ -47,7 +47,7 @@
</div>
</template>
</div>
<div class="clip-count" v-show="activeIndex !== index">
<div class="clip-count" v-show="isMultiple || activeIndex !== index">
{{ index + 1 }}
</div>
</div>
@ -66,9 +66,13 @@ const props = defineProps({
fullData: {
type: Object,
required: true
},
isMultiple: {
type: Boolean,
required: true
}
})
const emit = defineEmits(['onDataChange', 'onDataRemove'])
const emit = defineEmits(['onDataChange', 'onDataRemove', 'onSelectItemAdd'])
const isOverSizedContent = (item) => {
const { type, data } = item
if (type === 'text') {
@ -77,18 +81,42 @@ const isOverSizedContent = (item) => {
return JSON.parse(item.data).length >= 6
}
}
const selectItemList = ref([])
const emptySelectItemList = () => (selectItemList.value = [])
defineExpose({
selectItemList, // Main/Switch
emptySelectItemList
})
watch(
() => props.isMultiple,
(val) => {
if (!val) {
emptySelectItemList() // 退
}
}
)
const handleItemClick = (ev, item) => {
const { button } = ev
if (button === 0) {
//
window.copy(item)
window.paste()
} else if (button === 2) {
//
window.copy(item)
if (props.isMultiple === true) {
const index = selectItemList.value.indexOf(item)
console.log(index)
if (index !== -1) {
selectItemList.value.splice(index, 1) //
} else {
selectItemList.value.push(item) //
}
emit('onSelectItemAdd')
} else {
const { button } = ev
if (button === 0) {
//
window.copy(item)
window.paste()
} else if (button === 2) {
//
window.copy(item)
}
}
}
const handleContentExpand = (item) => emit('onDataChange', item)
const activeIndex = ref(0)
const handleMouseOver = (index) => (activeIndex.value = index)
const operation = [
@ -127,10 +155,7 @@ const handleOperateClick = ({ id, item }) => {
}
}
// getter
watch(
() => props.showList,
() => (activeIndex.value = 0)
)
watch(props.showList, () => (activeIndex.value = 0))
onMounted(() => {
//
document.addEventListener('keydown', (e) => {

View File

@ -16,6 +16,9 @@
background-color: @text-bg-color-lighter;
transition: all 0.15s;
}
&.select {
background-color: @text-bg-color-lighter;
}
.clip-info {
display: flex;
justify-content: center;

View File

@ -34,10 +34,25 @@
}
}
}
.clip-switch-btn {
width: 25px;
height: 25px;
padding: 10px;
cursor: pointer;
.clip-switch-btn-list {
margin-right: 5px;
.clip-switch-btn {
width: 25px;
height: 25px;
padding: 10px;
border-radius: 5px;
margin: 5px 2px;
cursor: pointer;
color: @text-color;
background-color: @nav-bg-color;
&:hover {
background-color: @nav-hover-bg-color;
transition: all 0.15s ease-in-out;
}
&.clip-select-count {
background-color: @primary-color;
color: @bg-color;
}
}
}
}

View File

@ -8,9 +8,26 @@
></ClipFullData>
<ClipSwitch ref="ClipSwitchRef" @onNavClick="handleNavClick">
<template #SidePanel>
<div v-show="!isSearchPanelExpand">
<span class="clip-switch-btn"> 👆 </span>
<span class="clip-switch-btn clip-search-btn" @click="handleSearchBtnClick"> 🔍 </span>
<div class="clip-switch-btn-list" v-show="!isSearchPanelExpand">
<span class="clip-switch-btn clip-select-count" v-show="isMultiple">
{{ selectCount }}
</span>
<span class="clip-switch-btn" v-show="isMultiple" @click="handleMultiCopyBtnClick(false)"
>📄 复制</span
>
<span class="clip-switch-btn" v-show="isMultiple" @click="handleMultiCopyBtnClick(true)"
>📑 粘贴</span
>
<span class="clip-switch-btn" @click="handleMultiBtnClick">{{
isMultiple ? '❌ 退出多选' : '👆'
}}</span>
<span
class="clip-switch-btn clip-search-btn"
v-show="!isMultiple"
@click="handleSearchBtnClick"
>
🔍
</span>
</div>
<ClipSearch
v-show="isSearchPanelExpand"
@ -23,8 +40,11 @@
<div class="clip-break"></div>
<div class="clip-empty-status" v-if="showList.length === 0">📪 无记录</div>
<ClipItemList
ref="ClipItemListRef"
:showList="showList"
:fullData="fullData"
:isMultiple="isMultiple"
@onSelectItemAdd="handleSelectItemAdd"
@onDataChange="toggleFullData"
@onDataRemove="handleDataRemove"
>
@ -42,13 +62,66 @@ import ClipFloatBtn from '../cpns/ClipFloatBtn.vue'
const isMultiple = ref(false)
const handleMultiBtnClick = () => {
isMultiple.value = !isMultiple.value
}
const isSearchPanelExpand = ref(false)
const handleSearchBtnClick = () => {
//
isSearchPanelExpand.value = true
nextTick(() => window.focus())
}
const ClipItemListRef = ref(null)
const selectCount = ref(0)
const handleSelectItemAdd = () => {
// item count
selectCount.value = ClipItemListRef.value.selectItemList.length
}
const handleMultiCopyBtnClick = (isPaste) => {
const itemList = ClipItemListRef.value.selectItemList
// /
const isMergeFile =
itemList.filter((item) => item.type === 'image' || item.type === 'file').length !== 0
if (isMergeFile) {
const filePathArray = []
itemList.map((item) => {
const { type } = item
if (type === 'text') {
const textFile = window.createFile(item)
filePathArray.push({
path: textFile
})
} else if (type === 'image') {
const imageFile = window.createFile(item)
filePathArray.push({
path: imageFile
})
} else {
// file
const files = JSON.parse(item.data)
filePathArray.push(...files)
}
})
window.copy({
type: 'file',
data: JSON.stringify(filePathArray)
})
} else {
const result = itemList.map((item) => item.data).join('\n')
window.copy({
type: 'text',
data: result
})
}
isPaste && window.paste()
ClipItemListRef.value.emptySelectItemList()
isMultiple.value = false
}
const GAP = 15 //
const offset = ref(0) //
const filterText = ref('') //