diff --git a/src/App.vue b/src/App.vue
index 313dbd5..cb94d98 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -6,6 +6,7 @@ import GLobalModal from './GlobalModal'
import SettingsButton from './settings/SettingsButton'
import SettingsOverlay from './settings/SettingsOverlay'
import Sider from './layout/sider'
+import Dock from './layout/dock'
import LoginModal from './user/LoginModal'
import { computed } from 'vue'
import asyncLoader from './utils/asyncLoader'
@@ -29,6 +30,7 @@ const layout = useLayoutStore()
+
diff --git a/src/fox/index.tsx b/src/fox/index.tsx
index 791b03a..64b2cc0 100644
--- a/src/fox/index.tsx
+++ b/src/fox/index.tsx
@@ -63,6 +63,20 @@ export default defineComponent(() => {
run('shuixing')
}
})
+ const handle = () => {
+ if (document.visibilityState === 'visible') {
+ sleep.value = false
+ run('shuixing')
+ }
+ }
+ let it: any = 0
+ const handleMove = () => {
+ clearTimeout(it)
+ sleep.value = false
+ it = setTimeout(() => {
+ sleep.value = true
+ }, 10000)
+ }
onMounted(async () => {
if (!container) return
await app.init({
@@ -86,26 +100,12 @@ export default defineComponent(() => {
},
{ immediate: true }
)
- const handle = () => {
- if (document.visibilityState === 'visible') {
- sleep.value = false
- run('shuixing')
- }
- }
window.addEventListener('visibilitychange', handle)
- let it: any = 0
- const handleMove = () => {
- clearTimeout(it)
- sleep.value = false
- it = setTimeout(() => {
- sleep.value = true
- }, 10000)
- }
window.addEventListener('mousemove', handleMove)
- onUnmounted(() => {
- window.removeEventListener('visibilitychange', handle)
- window.removeEventListener('mousemove', handleMove)
- })
+ })
+ onUnmounted(() => {
+ window.removeEventListener('visibilitychange', handle)
+ window.removeEventListener('mousemove', handleMove)
})
const router = useRouterStore()
diff --git a/src/layout/adder/CustomAdder.tsx b/src/layout/adder/CustomAdder.tsx
index 15ac15f..1feea76 100644
--- a/src/layout/adder/CustomAdder.tsx
+++ b/src/layout/adder/CustomAdder.tsx
@@ -4,7 +4,7 @@ import { Button, Form, Input, InputGroup } from 'ant-design-vue'
import { OhVueIcon, addIcons } from 'oh-vue-icons'
import { MdUpload, MdImage, MdCheck } from 'oh-vue-icons/icons'
import ImageUploader from '@/utils/ImageUploader'
-import useLink from './useLink'
+import useLink from '../../utils/useLink'
import { CheckOutlined } from '@ant-design/icons-vue'
import { v4 as uuid } from 'uuid'
import type { Block } from '../layout.types'
@@ -183,22 +183,36 @@ export default defineComponent(() => {
{form.type === 1 && (
<>
-
-
-
-
-
-
+
+
+
+
+
+
+
+
@@ -216,17 +230,17 @@ export default defineComponent(() => {
}
const id = uuid()
const data: Block = {
+ id,
link: form.link,
name: '',
- icon: form.type === 0 ? info.icon : '',
+ icon: form.type === 0 ? form.icon || info.icon : '',
background: form.type === 0 ? '' : form.background,
color: form.type === 0 ? '' : form.color,
+ w: 1,
+ h: 1,
text:
form.type === 0 ? '' : form.text || form.name.substring(0, 2).toLocaleUpperCase(),
- label: form.name,
- extra: {
- id
- }
+ label: form.name
}
layout.addBlock(data)
}}
diff --git a/src/layout/dock/index.tsx b/src/layout/dock/index.tsx
new file mode 100644
index 0000000..560baf8
--- /dev/null
+++ b/src/layout/dock/index.tsx
@@ -0,0 +1,55 @@
+import { computed, defineComponent } from 'vue'
+import useLayoutStore from '../useLayoutStore'
+import useSortable, { dragging } from '../grid/useSortable'
+import LinkBlock from '../grid/LinkBlock'
+
+export default defineComponent(() => {
+ const layout = useLayoutStore()
+ const container = useSortable(
+ computed(() => layout.state.dock),
+ 'dock'
+ )
+
+ return () => (
+
+ {layout.state.dockLabels.split('').map((l, i) => {
+ const block = layout.state.dock[i]
+ return (
+
e.preventDefault()}
+ onDrop={() => {
+ // 处理移入(当前有内容不可移入)
+ if (!dragging.id || block || !dragging.transportable) return
+ if (dragging.type === 'page') {
+ const oldIdx = layout.currentPage.list.findIndex((el) => el.id === dragging.id)
+ if (oldIdx < 0) return
+ const block = layout.currentPage.list[oldIdx]
+ layout.state.dock[i] = block
+ layout.currentPage.list.splice(oldIdx, 1)
+ return
+ }
+ if (dragging.type && dragging.type !== 'dock') {
+ // TODO: 文件夹
+ return
+ }
+ }}
+ >
+ {block &&
}
+
+ {l}
+
+
+ )
+ })}
+
+ )
+})
diff --git a/src/layout/grid/LinkBlock.tsx b/src/layout/grid/LinkBlock.tsx
new file mode 100644
index 0000000..552db9d
--- /dev/null
+++ b/src/layout/grid/LinkBlock.tsx
@@ -0,0 +1,26 @@
+import { defineComponent } from 'vue'
+import type { Block } from '../layout.types'
+
+export default defineComponent({
+ props: {
+ block: {
+ type: Object as () => Block,
+ required: true
+ }
+ },
+ setup(props) {
+ return () => (
+
+ )
+ }
+})
diff --git a/src/layout/grid/index.tsx b/src/layout/grid/index.tsx
index c9b55c0..ac294b9 100644
--- a/src/layout/grid/index.tsx
+++ b/src/layout/grid/index.tsx
@@ -1,52 +1,22 @@
-import { defineComponent, onMounted } from 'vue'
+import { computed, defineComponent, TransitionGroup } from 'vue'
import useLayoutStore from '../useLayoutStore'
import { OhVueIcon, addIcons } from 'oh-vue-icons'
import { MdAdd } from 'oh-vue-icons/icons'
import useRouterStore from '@/useRouterStore'
-import Sortable from 'sortablejs'
+import { globalToast } from '@/main'
+import LinkBlock from './LinkBlock'
+import useSortable, { dragging } from './useSortable'
addIcons(MdAdd)
export default defineComponent(() => {
const layout = useLayoutStore()
const router = useRouterStore()
- let container: HTMLDivElement | null = null
- onMounted(() => {
- if (!container) return
- new Sortable(container, {
- animation: 400,
- scroll: true,
- scrollSensitivity: 200,
- scrollSpeed: 10,
- invertSwap: true,
- invertedSwapThreshold: 1,
- filter: '.operation-button',
- ghostClass: 'opacity-20',
- dragClass: 'dragging-block',
- onStart: (e) => {
- // layout.moving.id = e.item.id
- // layout.moving.type = e.item.dataset?.type || ''
- },
- onMove: (e) => {
- if (e.related.className.includes('operation-button') && e.willInsertAfter) return false
- },
- onEnd: (e) => {
- // const oldPath = e.from.dataset.path
- // const newPath = e.to.dataset.path
- // if (e.oldIndex === undefined || e.newIndex === undefined || !oldPath || !newPath) return
- // const oldIndex = e.oldIndex
- // const newIndex = e.newIndex
- // const oldList = useSortList(oldPath as any)
- // const newList = useSortList(newPath as any)
- // const item = oldList[oldIndex]
- // if (!item || !isReactive(oldList) || !isReactive(newList)) return
- // oldList.splice(oldIndex, 1)
- // newList.splice(newIndex, 0, item)
- // layout.moving.id = ''
- // layout.moving.type = ''
- }
- })
- })
+ const container = useSortable(
+ computed(() => layout.currentPage.list),
+ 'page'
+ )
+
return () => (
{
layout.isCompact = false
}
}}
+ onDragover={(e) => e.preventDefault()}
+ onDrop={() => {
+ // 处理移入(当前有内容不可移入)
+ if (!dragging.id) return
+ if (dragging.type === 'dock') {
+ const oldIdx = layout.state.dock.findIndex((el) => el?.id === dragging.id)
+ if (oldIdx < 0) return
+ const block = layout.state.dock[oldIdx]
+ if (!block) return
+ layout.currentPage.list.push(block)
+ layout.state.dock[oldIdx] = null
+ return
+ }
+ if (dragging.type && dragging.type !== 'dock') {
+ // TODO: 文件夹
+ return
+ }
+ }}
>
(container = el as any)}
+ style="grid-template-columns:repeat(auto-fill, var(--block-size));grid-auto-rows:var(--block-size)"
+ ref={container}
>
+
+ {layout.currentPage.list.map((block, idx) => (
+ e.preventDefault()}
+ onDrop={() => {
+ // 处理移入(当前有内容不可移入)
+ if (!dragging.id) return
+ if (dragging.type === 'dock') {
+ const oldIdx = layout.state.dock.findIndex((el) => el?.id === dragging.id)
+ if (oldIdx < 0) return
+ const block = layout.state.dock[oldIdx]
+ if (!block) return
+ layout.currentPage.list.splice(idx, 0, block)
+ layout.state.dock[oldIdx] = null
+ return
+ }
+ if (dragging.type && dragging.type !== 'dock') {
+ // TODO: 文件夹
+ return
+ }
+ }}
+ >
+
+ {block.link ? (
+ block.link.startsWith('id:') ? (
+ // 文件夹
+
+ ) : (
+ // 链接
+
+ )
+ ) : (
+ // 小组件
+
+ )}
+
+
+ {block.label}
+
+
+ ))}
+
diff --git a/src/layout/grid/useSortable.ts b/src/layout/grid/useSortable.ts
new file mode 100644
index 0000000..3c25808
--- /dev/null
+++ b/src/layout/grid/useSortable.ts
@@ -0,0 +1,56 @@
+import Sortable from 'sortablejs'
+import { ref, watch, type Ref } from 'vue'
+
+export const dragging = {
+ type: '',
+ id: '',
+ transportable: false
+}
+
+export default function useSortable(list: Ref
, type = '') {
+ const container = ref(null)
+ watch(
+ container,
+ (el, _, onClean) => {
+ if (!el) return
+ const s = new Sortable(el, {
+ animation: 400,
+ scroll: true,
+ scrollSensitivity: 200,
+ scrollSpeed: 10,
+ ...(type === 'page'
+ ? {
+ invertSwap: true,
+ invertedSwapThreshold: 1,
+ filter: '.operation-button'
+ }
+ : {}),
+ ghostClass: 'opacity-20',
+ onStart: (e: any) => {
+ dragging.type = type
+ dragging.id = e.item.id || ''
+ // 只有链接才能被拖拽到其他区域
+ dragging.transportable = e.item.dataset?.transportable ? true : false
+ },
+ onMove: (e) => {
+ if (e.related.className.includes('operation-button') && e.willInsertAfter) return false
+ },
+ onEnd: (e) => {
+ // 同区域拖拽,直接交换位置
+ if (dragging.type !== type) return
+ const oldIdx = e.oldIndex
+ const newIdx = e.newIndex
+ if (oldIdx === undefined || newIdx === undefined) return
+ const oldItem = list.value[oldIdx]
+ list.value.splice(oldIdx, 1)
+ list.value.splice(newIdx, 0, oldItem)
+ }
+ })
+ onClean(() => {
+ s.destroy()
+ })
+ },
+ { flush: 'post' }
+ )
+ return container
+}
diff --git a/src/layout/layout.types.ts b/src/layout/layout.types.ts
index a431c63..cff394a 100644
--- a/src/layout/layout.types.ts
+++ b/src/layout/layout.types.ts
@@ -5,6 +5,7 @@ export enum BlockType {
}
export interface Block {
+ id: string
link: string // '' 代表小组件,id:xxx 代表文件夹,其他代表链接
// 内容标注,小组件表示展示何种小组件
name: string
@@ -18,6 +19,9 @@ export interface Block {
background: string
// 文字颜色
color: string
+ // 宽高
+ w: number
+ h: number
// 其他信息
extra?: any
}
@@ -45,5 +49,6 @@ export interface Layout {
Block | null,
Block | null
]
+ dockLabels: string
simple: boolean
}
diff --git a/src/layout/useLayoutStore.ts b/src/layout/useLayoutStore.ts
index 44d45dd..0176a25 100644
--- a/src/layout/useLayoutStore.ts
+++ b/src/layout/useLayoutStore.ts
@@ -15,6 +15,7 @@ const defaultLayout: Layout = {
currentPage: 0,
dir: {},
dock: [null, null, null, null, null, null, null, null, null, null],
+ dockLabels: 'QWERASDFGB',
simple: false
}
@@ -71,6 +72,7 @@ export default defineStore('layout', () => {
pageList.push(block)
globalToast.success('添加成功')
}
+
return {
state,
ready,
diff --git a/src/layout/adder/useLink.ts b/src/utils/useLink.ts
similarity index 100%
rename from src/layout/adder/useLink.ts
rename to src/utils/useLink.ts