From cfe567b9fb8212112df80c51c0203dcbbe193b82 Mon Sep 17 00:00:00 2001
From: ZiuChen <457353192@qq.com>
Date: Thu, 8 Sep 2022 23:49:11 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=A4=9A=E9=80=89?=
=?UTF-8?q?=E5=8A=9F=E8=83=BD=20=E6=94=AF=E6=8C=81=E5=90=88=E5=B9=B6?=
=?UTF-8?q?=E6=96=87=E6=9C=AC/=E5=9B=BE=E7=89=87/=E6=96=87=E4=BB=B6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
public/preload.js | 32 ++++++++++--
src/cpns/ClipItemList.vue | 59 +++++++++++++++-------
src/style/cpns/clip-item-list.less | 3 ++
src/style/cpns/clip-switch.less | 25 ++++++++--
src/views/Main.vue | 79 ++++++++++++++++++++++++++++--
5 files changed, 170 insertions(+), 28 deletions(-)
diff --git a/public/preload.js b/public/preload.js
index 40c9fc3..c7e3491 100644
--- a/public/preload.js
+++ b/public/preload.js
@@ -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
diff --git a/src/cpns/ClipItemList.vue b/src/cpns/ClipItemList.vue
index a5ee289..37fc496 100644
--- a/src/cpns/ClipItemList.vue
+++ b/src/cpns/ClipItemList.vue
@@ -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 }"
>
-
+
@@ -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) => {
diff --git a/src/style/cpns/clip-item-list.less b/src/style/cpns/clip-item-list.less
index 9aaf700..6a27ee2 100644
--- a/src/style/cpns/clip-item-list.less
+++ b/src/style/cpns/clip-item-list.less
@@ -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;
diff --git a/src/style/cpns/clip-switch.less b/src/style/cpns/clip-switch.less
index 50f7fa6..2e40d61 100644
--- a/src/style/cpns/clip-switch.less
+++ b/src/style/cpns/clip-switch.less
@@ -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;
+ }
+ }
}
}
diff --git a/src/views/Main.vue b/src/views/Main.vue
index 494beaa..bc0965f 100644
--- a/src/views/Main.vue
+++ b/src/views/Main.vue
@@ -8,9 +8,26 @@
>
-
-
👆
-
🔍
+
+
+ {{ selectCount }}
+
+ 📄 复制
+ 📑 粘贴
+ {{
+ isMultiple ? '❌ 退出多选' : '👆'
+ }}
+
+ 🔍
+
📪 无记录
@@ -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('') // 搜索框绑定值