feat: 拆分组件 用Vue3重写Main.vue

This commit is contained in:
ZiuChen 2022-08-15 15:18:19 +08:00
parent 94161453b4
commit 6b63c7a4f7
7 changed files with 263 additions and 249 deletions

View File

@ -13,7 +13,7 @@
</template>
<script setup>
import FileListVue from './FileList.vue'
import FileList from './FileList.vue'
const props = defineProps({
isShow: {

22
src/cpns/ClipSearch.vue Normal file
View File

@ -0,0 +1,22 @@
<template>
<div class="clip-search">
<input v-model="filterText" autofocus type="text" placeholder="输入关键词检索" />
</div>
</template>
<script setup>
import { ref, watch } from 'vue'
const filterText = ref('')
const props = defineProps({
modelValue: {
type: String,
required: true
}
})
const emit = defineEmits(['update:modelValue'])
watch(filterText, (val) => emit('update:modelValue', val))
</script>
<style lang="less" scoped>
@import '../style/cpns/clip-search.less';
</style>

42
src/cpns/ClipSwitch.vue Normal file
View File

@ -0,0 +1,42 @@
<template>
<div class="clip-switch">
<div class="clip-switch-items">
<template v-for="tab of tabs">
<div
:class="{ 'clip-switch-item': true, active: activeTab === tab.type }"
@click="onNavClick(tab.type)"
>
{{ tab.name }}
</div>
</template>
</div>
<slot name="SidePanel"></slot>
</div>
</template>
<script setup>
import { ref } from 'vue'
const tabs = ref([
{ name: '📚 全部', type: 'all' },
{ name: '📋 文字', type: 'text' },
{ name: '⛺ 图片', type: 'image' },
{ name: '📂 文件', type: 'file' }
])
const activeTab = ref('all')
const emit = defineEmits(['onNavClick'])
const toggleNav = (type) => (activeTab.value = type)
const onNavClick = (type) => {
toggleNav(type)
emit('onNavClick', type)
}
defineExpose({
tabs,
activeTab,
toggleNav
})
</script>
<style lang="less" scoped>
@import '../style/cpns/clip-switch.less';
</style>

View File

@ -7,24 +7,15 @@
</div>
</template>
<script>
export default {
name: 'fileList',
props: {
data: {
type: Array,
required: true
}
},
methods: {
openFile(path) {
window.openFile(path)
},
getIcon(path) {
return window.getIcon(path)
}
<script setup>
const props = defineProps({
data: {
type: Array,
required: true
}
}
})
const openFile = (path) => window.openFile(path)
const getIcon = (path) => window.getIcon(path)
</script>
<style lang="less" scoped>

View File

@ -0,0 +1,22 @@
@import '../variable.less';
.clip-search {
width: 40%;
margin-right: 30px;
input {
width: 100%;
/* normalize */
background: none;
outline: none;
border: none;
/* custom */
color: @text-color;
background-color: white;
height: fit-content;
font-size: 15px;
padding: 10px;
border-radius: 5px;
&::placeholder {
color: @text-color-lighter;
}
}
}

View File

@ -0,0 +1,34 @@
@import '../variable.less';
.clip-switch {
z-index: 999;
position: fixed;
top: 0px;
display: flex;
justify-content: space-between;
align-items: center;
flex-direction: row;
width: 100%;
background-color: #eeeeee;
.active {
color: @primary-color;
background-color: white;
transition: all 0.15s ease-in-out;
}
.clip-switch-items {
display: flex;
justify-content: left;
align-items: center;
flex-direction: row;
.clip-switch-item {
padding: 10px 15px 10px 15px;
margin: 10px 5px 10px 10px;
cursor: pointer;
border-radius: 5px;
font-size: 14px;
&:hover {
background-color: rgb(222, 222, 222);
transition: all 0.15s ease-in-out;
}
}
}
}

View File

@ -6,190 +6,155 @@
:fullData="fullData"
@onOverlayClick="toggleFullData('')"
></ClipFullData>
<div class="clip-switch">
<div class="clip-switch-items">
<template v-for="tab of tabs">
<div
:class="{ 'clip-switch-item': true, active: activeTab === tab.type }"
@click="toggleNav(tab.type)"
>
{{ tab.name }}
</div>
</template>
</div>
<div class="clip-search">
<input v-model="filterText" autofocus type="text" placeholder="输入关键词检索" />
</div>
</div>
<ClipSwitch ref="ClipSwitchRef" @onNavClick="updateShowList">
<template #SidePanel>
<ClipSearch v-model="filterText"></ClipSearch>
</template>
</ClipSwitch>
<div class="clip-break"></div>
<div class="clip-empty-status" v-if="showList.length === 0">📪 无记录</div>
<ClipItemList :showList="showList" @onDataChange="toggleFullData"></ClipItemList>
<ClipItemList :showList="showList" @onDataChange="toggleFullData"> </ClipItemList>
</div>
</template>
<script>
import FileList from '../cpns/FileList.vue'
<script setup>
import { ref, watch, onMounted, computed } from 'vue'
import ClipItemList from '../cpns/ClipItemList.vue'
import ClipFullData from '../cpns/ClipFullData.vue'
import ClipSearch from '../cpns/ClipSearch.vue'
import ClipSwitch from '../cpns/ClipSwitch.vue'
export default {
name: 'Main',
components: {
FileList,
ClipItemList,
ClipFullData
},
const ClipSwitchRef = ref()
data() {
return {
GAP: 10,
offset: 0,
showList: [],
list: [],
fullData: { type: 'text', data: '' },
fullDataShow: false,
filterText: '',
tabs: [
{
name: '📚 全部',
type: 'all'
},
{
name: '📋 文字',
type: 'text'
},
{
name: '⛺ 图片',
type: 'image'
},
{
name: '📂 文件',
type: 'file'
}
],
activeTab: 'all'
const fullData = ref({ type: 'text', data: '' })
const fullDataShow = ref(false)
const filterText = ref('')
const GAP = 10
const list = ref([])
const showList = ref([])
const offset = ref(0)
const updateShowList = (type) => {
if (type === 'all') {
if (filterText.value) {
//
showList.value = list.value
.filter((item) => item.type !== 'image')
.filter((item) => item.data.indexOf(filterText.value) !== -1)
.slice(0, GAP)
} else {
//
showList.value = list.value
.filter((item) => item.data.indexOf(filterText.value) !== -1)
.slice(0, GAP)
}
},
watch: {
filterText: function (val) {
this.updateShowList()
}
},
mounted: function () {
this.list = window.db.dataBase.data
this.showList = this.list.slice(0, this.GAP) // 10
} else if (type === 'image') {
// DataURL
showList.value = list.value.filter((item) => item.type === type).slice(0, GAP)
} else {
// `file` stringifydata
// `text` data
showList.value = list.value
.filter((item) => item.type === type)
.filter((item) => item.data.indexOf(filterText.value) !== -1)
.slice(0, GAP)
}
document.scrollingElement.scrollTop = 0
}
//
this.toggleNav(this.activeTab)
const toggleFullData = (item) => {
// only text || file
const { type, data } = item
if (type === 'text') {
fullData.value.type = 'text'
fullData.value.data = data
} else if (type === 'file') {
fullData.value.type = 'file'
fullData.value.data = JSON.parse(data)
}
fullDataShow.value = !fullDataShow.value
}
//
let prev = {}
setInterval(() => {
const now = window.db.dataBase.data[0]
if (prev?.id === now?.id) {
} else {
//
this.list = window.db.dataBase.data
this.toggleNav(this.activeTab)
prev = now
}
}, 500)
//
const callBack = (e) => {
const { scrollTop, clientHeight, scrollHeight } = e.target.scrollingElement
if (scrollTop + clientHeight + 20 >= scrollHeight) {
this.offset += this.GAP + 1
let addition = []
if (this.activeTab !== 'all') {
addition = this.list.filter((item) => item.type === this.activeTab)
} else {
addition = this.list
}
addition = addition.slice(this.offset, this.offset + this.GAP)
if (addition.length) {
this.showList.push(...addition)
}
}
}
document.addEventListener('scroll', callBack)
document.addEventListener('keydown', (e) => {
const { key, ctrlKey } = e
const isTab = key === 'Tab'
const isSearch =
key === '/' ||
(ctrlKey && (key === 'F' || key === 'f')) ||
(ctrlKey && (key === 'L' || key === 'l'))
const isExit = key === 'Escape'
if (isTab) {
const l = ['all', 'text', 'image', 'file']
const i = l.indexOf(this.activeTab)
const t = i === l.length - 1 ? l[0] : l[i + 1]
this.toggleNav(t)
} else if (isSearch) {
document.querySelector('input').focus()
} else if (isExit) {
this.filterText = ''
}
})
},
methods: {
toggleNav(type) {
//
this.activeTab = type
this.updateShowList()
},
updateShowList(type = this.activeTab) {
if (type === 'all') {
if (this.filterText) {
//
this.showList = this.list
.filter((item) => item.type !== 'image')
.filter((item) => item.data.indexOf(this.filterText) !== -1)
.slice(0, this.GAP)
} else {
//
this.showList = this.list
.filter((item) => item.data.indexOf(this.filterText) !== -1)
.slice(0, this.GAP)
}
} else if (type === 'image') {
// DataURL
this.showList = this.list.filter((item) => item.type === type).slice(0, this.GAP)
} else {
// `file` stringifydata
// `text` data
this.showList = this.list
.filter((item) => item.type === type)
.filter((item) => item.data.indexOf(this.filterText) !== -1)
.slice(0, this.GAP)
}
document.scrollingElement.scrollTop = 0
},
toggleFullData(item) {
// only text || file
const { type, data } = item
if (type === 'text') {
this.fullData.type = 'text'
this.fullData.data = data
} else if (type === 'file') {
this.fullData.type = 'file'
this.fullData.data = JSON.parse(data)
}
this.fullDataShow = !this.fullDataShow
},
restoreDataBase() {
const flag = window.confirm('确定要清空剪贴板记录吗?')
if (flag) {
window.db.emptyDataBase()
this.updateShowList()
}
}
const restoreDataBase = () => {
const flag = window.confirm('确定要清空剪贴板记录吗?')
if (flag) {
window.db.emptyDataBase()
updateShowList('all')
}
}
onMounted(() => {
// Ref
const activeTab = computed(() => ClipSwitchRef.value.activeTab)
//
list.value = window.db.dataBase.data
showList.value = list.value.slice(0, GAP) // 10
updateShowList(activeTab.value)
//
let prev = {}
setInterval(() => {
const now = window.db.dataBase.data[0]
if (prev?.id === now?.id) {
} else {
//
list.value = window.db.dataBase.data
updateShowList(activeTab.value)
prev = now
}
}, 500)
//
watch(filterText, (val) => {
console.log(val)
console.log(ClipSwitchRef)
updateShowList(activeTab.value)
})
//
document.addEventListener('scroll', (e) => {
const { scrollTop, clientHeight, scrollHeight } = e.target.scrollingElement
if (scrollTop + clientHeight + 20 >= scrollHeight) {
offset.value += GAP + 1
let addition = []
if (activeTab.value !== 'all') {
addition = list.value.filter((item) => item.type === activeTab.value)
} else {
addition = list.value
}
addition = addition.slice(offset.value, offset.value + GAP)
if (addition.length) {
showList.value.push(...addition)
}
}
})
//
document.addEventListener('keydown', (e) => {
const { key, ctrlKey } = e
const isTab = key === 'Tab'
const isSearch =
key === '/' ||
(ctrlKey && (key === 'F' || key === 'f')) ||
(ctrlKey && (key === 'L' || key === 'l'))
const isExit = key === 'Escape'
if (isTab) {
const list = ['all', 'text', 'image', 'file']
const index = list.indexOf(activeTab.value)
const target = index === list.length - 1 ? list[0] : list[index + 1]
updateShowList(target)
} else if (isSearch) {
document.querySelector('input').focus()
} else if (isExit) {
filterText.value = ''
}
})
})
</script>
<style lang="less">
<style lang="less" scoped>
@import '../style/variable.less';
.clip-restore {
display: flex;
@ -210,60 +175,7 @@ export default {
transition: all 0.15s;
}
}
.clip-switch {
z-index: 999;
position: fixed;
top: 0px;
display: flex;
justify-content: space-between;
align-items: center;
flex-direction: row;
width: 100%;
background-color: #eeeeee;
.active {
color: @primary-color;
background-color: white;
transition: all 0.15s ease-in-out;
}
.clip-switch-items {
display: flex;
justify-content: left;
align-items: center;
flex-direction: row;
.clip-switch-item {
padding: 10px 15px 10px 15px;
margin: 10px 5px 10px 10px;
cursor: pointer;
border-radius: 5px;
font-size: 14px;
&:hover {
background-color: rgb(222, 222, 222);
transition: all 0.15s ease-in-out;
}
}
}
}
.clip-search {
width: 40%;
margin-right: 30px;
input {
width: 100%;
/* normalize */
background: none;
outline: none;
border: none;
/* custom */
color: @text-color;
background-color: white;
height: fit-content;
font-size: 15px;
padding: 10px;
border-radius: 5px;
&::placeholder {
color: @text-color-lighter;
}
}
}
.clip-break {
height: 55px;
}
@ -275,13 +187,4 @@ export default {
align-items: center;
margin-top: 50px;
}
// .fade-enter-active,
// .fade-leave-active {
// transition: all 0.15s;
// }
// .fade-enter,
// .fade-leave-to {
// opacity: 0;
// }
</style>