From 550993cce5f557aa7865a52359a76131383dbabd Mon Sep 17 00:00:00 2001 From: plightfield <1207120484@qq.com> Date: Wed, 25 Sep 2024 14:25:44 +0800 Subject: [PATCH] change --- src/App.vue | 2 + src/fox/index.tsx | 36 +++---- src/layout/adder/CustomAdder.tsx | 58 +++++++---- src/layout/dock/index.tsx | 55 ++++++++++ src/layout/grid/LinkBlock.tsx | 26 +++++ src/layout/grid/index.tsx | 136 +++++++++++++++++-------- src/layout/grid/useSortable.ts | 56 ++++++++++ src/layout/layout.types.ts | 5 + src/layout/useLayoutStore.ts | 2 + src/{layout/adder => utils}/useLink.ts | 0 10 files changed, 293 insertions(+), 83 deletions(-) create mode 100644 src/layout/dock/index.tsx create mode 100644 src/layout/grid/LinkBlock.tsx create mode 100644 src/layout/grid/useSortable.ts rename src/{layout/adder => utils}/useLink.ts (100%) 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 () => ( +
+
{props.block.text}
+
+ ) + } +}) 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} +
+
+ ))} +
{ - router.path = 'global-adder' + if (layout.state.content[layout.state.current].pages[layout.state.currentPage]) { + router.path = 'global-adder' + } else { + globalToast.warning('请先添加页面') + } }} > - + + +
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