等待开发组件布局
This commit is contained in:
parent
1e9a05ca33
commit
ceb05a4f62
|
@ -23,6 +23,7 @@
|
||||||
"oh-vue-icons": "^1.0.0-rc3",
|
"oh-vue-icons": "^1.0.0-rc3",
|
||||||
"pinia": "^2.1.7",
|
"pinia": "^2.1.7",
|
||||||
"pinia-plugin-persistedstate": "^3.2.3",
|
"pinia-plugin-persistedstate": "^3.2.3",
|
||||||
|
"sortablejs": "^1.15.3",
|
||||||
"ua-parser-js": "^1.0.38",
|
"ua-parser-js": "^1.0.38",
|
||||||
"uuid": "^10.0.0",
|
"uuid": "^10.0.0",
|
||||||
"v-viewer": "^3.0.13",
|
"v-viewer": "^3.0.13",
|
||||||
|
@ -34,6 +35,7 @@
|
||||||
"@tsconfig/node20": "^20.1.4",
|
"@tsconfig/node20": "^20.1.4",
|
||||||
"@types/ali-oss": "^6.16.11",
|
"@types/ali-oss": "^6.16.11",
|
||||||
"@types/node": "^20.14.5",
|
"@types/node": "^20.14.5",
|
||||||
|
"@types/sortablejs": "^1.15.8",
|
||||||
"@types/ua-parser-js": "^0.7.39",
|
"@types/ua-parser-js": "^0.7.39",
|
||||||
"@types/uuid": "^10.0.0",
|
"@types/uuid": "^10.0.0",
|
||||||
"@vitejs/plugin-vue": "^5.0.5",
|
"@vitejs/plugin-vue": "^5.0.5",
|
||||||
|
|
|
@ -8,11 +8,15 @@ import SettingsOverlay from './settings/SettingsOverlay'
|
||||||
import Sider from './layout/sider'
|
import Sider from './layout/sider'
|
||||||
import LoginModal from './user/LoginModal'
|
import LoginModal from './user/LoginModal'
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
|
import asyncLoader from './utils/asyncLoader'
|
||||||
|
import useLayoutStore from './layout/useLayoutStore'
|
||||||
|
const Grid = asyncLoader(() => import('./layout/grid'))
|
||||||
const settings = useSettingsStore()
|
const settings = useSettingsStore()
|
||||||
const blockSize = computed(() => settings.state.blockSize + 'rem')
|
const blockSize = computed(() => settings.state.blockSize + 'rem')
|
||||||
const blockPadding = computed(() => settings.state.blockPadding + 'rem')
|
const blockPadding = computed(() => settings.state.blockPadding + 'rem')
|
||||||
const mainWidth = computed(() => settings.state.mainWidth + '%')
|
const mainWidth = computed(() => settings.state.mainWidth + '%')
|
||||||
const blockRadius = computed(() => settings.state.blockRadius + 'rem')
|
const blockRadius = computed(() => settings.state.blockRadius)
|
||||||
|
const layout = useLayoutStore()
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="fixed left-0 top-0 w-full h-screen style-root" @contextmenu.prevent>
|
<div class="fixed left-0 top-0 w-full h-screen style-root" @contextmenu.prevent>
|
||||||
|
@ -23,6 +27,7 @@ const blockRadius = computed(() => settings.state.blockRadius + 'rem')
|
||||||
<SettingsButton />
|
<SettingsButton />
|
||||||
<Sider />
|
<Sider />
|
||||||
<LoginModal />
|
<LoginModal />
|
||||||
|
<Grid v-if="layout.ready" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import asyncLoader from './utils/asyncLoader'
|
||||||
addIcons(MdClose, MdOpeninfull, MdClosefullscreen)
|
addIcons(MdClose, MdOpeninfull, MdClosefullscreen)
|
||||||
const SearchPage = asyncLoader(() => import('@/layout/header/search/SearchPage'))
|
const SearchPage = asyncLoader(() => import('@/layout/header/search/SearchPage'))
|
||||||
|
|
||||||
const noFullList: RouteStr[] = ['global-search']
|
const noFullList: RouteStr[] = ['global-search', 'global-adder']
|
||||||
|
|
||||||
export default defineComponent(() => {
|
export default defineComponent(() => {
|
||||||
const router = useRouterStore()
|
const router = useRouterStore()
|
||||||
|
@ -21,7 +21,7 @@ export default defineComponent(() => {
|
||||||
full.value = false
|
full.value = false
|
||||||
})
|
})
|
||||||
return () => (
|
return () => (
|
||||||
<div class="fixed left-0 top-0 z-20 w-full">
|
<div class="fixed left-0 top-0 z-50 w-full">
|
||||||
{/* 背景遮罩 */}
|
{/* 背景遮罩 */}
|
||||||
<Transition>
|
<Transition>
|
||||||
{show.value && (
|
{show.value && (
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
import { defineComponent, onMounted } 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'
|
||||||
|
|
||||||
|
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 = ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return () => (
|
||||||
|
<div
|
||||||
|
class="absolute left-0 top-0 w-full h-screen overflow-y-auto no-scrollbar pt-[240px] px-[calc((100%_-_var(--main-width))_/_2)]"
|
||||||
|
onScroll={(e) => {
|
||||||
|
const h = (e.target as any).scrollTop
|
||||||
|
if (h > 60) {
|
||||||
|
// 需要移动搜索框和时间
|
||||||
|
layout.isCompact = true
|
||||||
|
} else {
|
||||||
|
layout.isCompact = false
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="w-full grid justify-center grid-flow-row-dense pb-[200px]"
|
||||||
|
style="grid-template-columns:repeat(auto-fill, var(--block-size));grid-auto-rows:var(--block-size);grid-template-rows:var(--block-size)"
|
||||||
|
ref={(el) => (container = el as any)}
|
||||||
|
>
|
||||||
|
<div class="w-full h-full flex justify-center items-center p-[var(--block-padding)] relative operation-button">
|
||||||
|
<div
|
||||||
|
class="w-full h-full overflow-hidden rounded-[calc(var(--block-radius)_*_var(--block-size))] bg-white/80 backdrop-blur flex justify-center items-center cursor-pointer hover:scale-105 transition-all"
|
||||||
|
onClick={() => {
|
||||||
|
router.path = 'global-adder'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<OhVueIcon name="md-add" fill="white" scale={2} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})
|
|
@ -2,6 +2,7 @@ import useSettingsStore from '@/settings/useSettingsStore'
|
||||||
import useTimeStore from '@/utils/useTimeStore'
|
import useTimeStore from '@/utils/useTimeStore'
|
||||||
import { Lunar } from 'lunar-typescript'
|
import { Lunar } from 'lunar-typescript'
|
||||||
import { computed, defineComponent, Transition } from 'vue'
|
import { computed, defineComponent, Transition } from 'vue'
|
||||||
|
import useLayoutStore from '../useLayoutStore'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
setup() {
|
setup() {
|
||||||
|
@ -28,21 +29,39 @@ export default defineComponent({
|
||||||
dayWeek
|
dayWeek
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const settings = useSettingsStore()
|
const settings = useSettingsStore()
|
||||||
|
const layout = useLayoutStore()
|
||||||
return () => (
|
return () => (
|
||||||
<div class="shadow-text tracking-widest font-mono text-white/80 font-bold">
|
<div
|
||||||
|
class="absolute z-20 shadow-text tracking-widest font-mono font-bold h-[110px] transition-all"
|
||||||
|
style={{
|
||||||
|
color: layout.isCompact ? 'white' : 'rgba(255,255,255,.8)',
|
||||||
|
transitionDuration: '.4s',
|
||||||
|
left: layout.isCompact ? '20px' : '50%',
|
||||||
|
top: layout.isCompact ? '20px' : '50px',
|
||||||
|
transform: layout.isCompact ? '' : 'translate(-50%,0)'
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Transition>
|
<Transition>
|
||||||
{settings.state.showDate && (
|
{settings.state.showDate && (
|
||||||
<div class="text-[4rem] leading-[4rem]">{text.value.timeStr}</div>
|
<div
|
||||||
|
class={
|
||||||
|
'transition-all ' +
|
||||||
|
(layout.isCompact ? 'text-[1.4rem] leading-[1.4rem]' : 'text-[4rem] leading-[4rem]')
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{text.value.timeStr}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</Transition>
|
</Transition>
|
||||||
<Transition>
|
<Transition>
|
||||||
{settings.state.showTime && (
|
{settings.state.showTime && (
|
||||||
<div class="flex justify-center items-center gap-4 mt-4">
|
<div
|
||||||
|
class={'flex items-center gap-4 mt-4 ' + (layout.isCompact ? '' : 'justify-center')}
|
||||||
|
>
|
||||||
<div>{text.value.dateStr}</div>
|
<div>{text.value.dateStr}</div>
|
||||||
<div>星期{info.value.dayWeek}</div>
|
<Transition>{!layout.isCompact && <div>星期{info.value.dayWeek}</div>}</Transition>
|
||||||
<div>{info.value.day}</div>
|
<Transition>{!layout.isCompact && <div>{info.value.day}</div>}</Transition>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Transition>
|
</Transition>
|
||||||
|
|
|
@ -9,25 +9,8 @@ export default defineComponent({
|
||||||
setup() {
|
setup() {
|
||||||
return () => (
|
return () => (
|
||||||
<>
|
<>
|
||||||
<div
|
<GlobalTime />
|
||||||
class="absolute z-20"
|
<Search />
|
||||||
style={{
|
|
||||||
left: '50%',
|
|
||||||
top: '4rem',
|
|
||||||
transform: 'translate(-50%,0)'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<GlobalTime />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="absolute left-1/2 -translate-x-1/2 z-20"
|
|
||||||
style={{
|
|
||||||
top: '12rem'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Search />
|
|
||||||
</div>
|
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,15 +5,18 @@ import useSearchConfigStore from './useSearchConfigStore'
|
||||||
import SearchConfig from './SearchConfig'
|
import SearchConfig from './SearchConfig'
|
||||||
import SearchHistory from './SearchHistory'
|
import SearchHistory from './SearchHistory'
|
||||||
import SearchSuggestion from './SearchSuggestion'
|
import SearchSuggestion from './SearchSuggestion'
|
||||||
|
import useLayoutStore from '@/layout/useLayoutStore'
|
||||||
|
|
||||||
export default defineComponent(() => {
|
export default defineComponent(() => {
|
||||||
const settings = useSettingsStore()
|
const settings = useSettingsStore()
|
||||||
const search = useSearchStore()
|
const search = useSearchStore()
|
||||||
const searchConfig = useSearchConfigStore()
|
const searchConfig = useSearchConfigStore()
|
||||||
|
const layout = useLayoutStore()
|
||||||
return () => (
|
return () => (
|
||||||
<div
|
<div
|
||||||
class="relative"
|
class="absolute left-1/2 -translate-x-1/2 z-20 transition-all"
|
||||||
style={{
|
style={{
|
||||||
|
top: layout.isCompact ? '40px' : '172px',
|
||||||
width: settings.state.searchWidth + 'rem'
|
width: settings.state.searchWidth + 'rem'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
|
@ -42,5 +42,4 @@ export interface Layout {
|
||||||
Block | null
|
Block | null
|
||||||
]
|
]
|
||||||
simple: boolean
|
simple: boolean
|
||||||
loading: boolean
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { OhVueIcon, addIcons } from 'oh-vue-icons'
|
||||||
import { PxHeadset, PxAddBox, PxCheck } from 'oh-vue-icons/icons'
|
import { PxHeadset, PxAddBox, PxCheck } from 'oh-vue-icons/icons'
|
||||||
import useRouterStore from '@/useRouterStore'
|
import useRouterStore from '@/useRouterStore'
|
||||||
import useLayoutStore from '../useLayoutStore'
|
import useLayoutStore from '../useLayoutStore'
|
||||||
|
import useUserStore from '@/user/useUserStore'
|
||||||
|
|
||||||
initIcons()
|
initIcons()
|
||||||
addIcons(PxHeadset, PxAddBox, PxCheck)
|
addIcons(PxHeadset, PxAddBox, PxCheck)
|
||||||
|
@ -57,6 +58,7 @@ export default defineComponent(() => {
|
||||||
const selected = ref(icons[0])
|
const selected = ref(icons[0])
|
||||||
const router = useRouterStore()
|
const router = useRouterStore()
|
||||||
const layout = useLayoutStore()
|
const layout = useLayoutStore()
|
||||||
|
const user = useUserStore()
|
||||||
const label = ref('')
|
const label = ref('')
|
||||||
watch(
|
watch(
|
||||||
selected,
|
selected,
|
||||||
|
@ -66,106 +68,114 @@ export default defineComponent(() => {
|
||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
)
|
)
|
||||||
return () => (
|
return () => (
|
||||||
<div class="fixed left-6 top-1/2 -translate-y-1/2 h-[600px] z-30">
|
<Transition>
|
||||||
<div class="w-[56px] h-full rounded-[28px] bg-black/70 backdrop-blur flex flex-col justify-between items-center">
|
{layout.ready && (
|
||||||
<ModeSwitch />
|
<div class="fixed left-6 top-1/2 -translate-y-1/2 h-[600px] z-30">
|
||||||
<div class="w-full h-[64px]" />
|
<div class="w-[56px] h-full rounded-[28px] bg-black/70 backdrop-blur flex flex-col justify-between items-center">
|
||||||
<div class="w-full h-0 flex-grow overflow-auto relative no-scrollbar">
|
<ModeSwitch />
|
||||||
<TransitionGroup>
|
<div class="w-full h-[64px]" />
|
||||||
{layout.state.content[layout.state.current].map((el, idx) => (
|
<div class="w-full h-0 flex-grow overflow-auto relative no-scrollbar">
|
||||||
<Item
|
<TransitionGroup>
|
||||||
key={idx}
|
{layout.state.content[layout.state.current].map((el, idx) => (
|
||||||
name={el.name}
|
<Item
|
||||||
label={el.label}
|
key={idx}
|
||||||
idx={idx}
|
name={el.name}
|
||||||
active={layout.state.currentPage === idx}
|
label={el.label}
|
||||||
onClick={() => {
|
idx={idx}
|
||||||
layout.state.currentPage = idx
|
active={layout.state.currentPage === idx}
|
||||||
}}
|
onClick={() => {
|
||||||
/>
|
layout.state.currentPage = idx
|
||||||
))}
|
}}
|
||||||
</TransitionGroup>
|
/>
|
||||||
<Transition>
|
))}
|
||||||
{layout.state.content[layout.state.current]?.length > 0 && (
|
</TransitionGroup>
|
||||||
|
<Transition>
|
||||||
|
{layout.state.content[layout.state.current]?.length > 0 && (
|
||||||
|
<div
|
||||||
|
class="absolute w-full h-[56px] rounded-lg bg-white/40 left-0 transition-all"
|
||||||
|
style={{
|
||||||
|
transitionDuration: '.3s',
|
||||||
|
top: `${layout.state.currentPage * 56}px`
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Transition>
|
||||||
|
</div>
|
||||||
|
<div class="w-full h-4" />
|
||||||
|
<Item
|
||||||
|
name="px-add-box"
|
||||||
|
label="添加"
|
||||||
|
onClick={() => {
|
||||||
|
showEdit.value = true
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Item
|
||||||
|
name="px-headset"
|
||||||
|
label="反馈"
|
||||||
|
onClick={() => {
|
||||||
|
router.path = 'settings-fallback'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="w-[56px] h-[56px] bg-white/40 rounded-full border-white border-[2px] border-solid overflow-hidden cursor-pointer"
|
||||||
|
onClick={() => {
|
||||||
|
if (user.isLogin) {
|
||||||
|
router.path = 'settings-user'
|
||||||
|
} else {
|
||||||
|
router.path = 'global-login'
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
{/* 添加页面 */}
|
||||||
|
<Transition name="page-adder">
|
||||||
|
{showEdit.value && (
|
||||||
<div
|
<div
|
||||||
class="absolute w-full h-[56px] rounded-lg bg-white/40 left-0 transition-all"
|
class="absolute left-[70px] bottom-0 w-56 rounded-lg p-4 bg-white/40 backdrop-blur shadow-lg"
|
||||||
style={{
|
v-outside-click={() => {
|
||||||
transitionDuration: '.3s',
|
showEdit.value = false
|
||||||
top: `${layout.state.currentPage * 56}px`
|
|
||||||
}}
|
}}
|
||||||
/>
|
>
|
||||||
|
<input
|
||||||
|
class="rounded bg-black/10 text-center text-sm w-full py-1"
|
||||||
|
v-model={label.value}
|
||||||
|
maxlength={2}
|
||||||
|
/>
|
||||||
|
<div class="flex flex-wrap gap-1 mt-2">
|
||||||
|
{icons.map((el) => (
|
||||||
|
<div
|
||||||
|
class={
|
||||||
|
'p-1 rounded cursor-pointer transition-all ' +
|
||||||
|
(selected.value.name === el.name
|
||||||
|
? 'text-black/80 bg-white shadow'
|
||||||
|
: 'text-black/60 hover:shadow hover:text-black/80 hover:bg-white')
|
||||||
|
}
|
||||||
|
onClick={() => {
|
||||||
|
selected.value = { ...el }
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<OhVueIcon name={el.name} fill="black" scale={1.3} />
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="w-full mt-2 py-1 rounded-lg bg-white text-center text-sm shadow hover:shadow-lg transition-all cursor-pointer"
|
||||||
|
onClick={() => {
|
||||||
|
layout.state.content[layout.state.current].push({
|
||||||
|
list: [],
|
||||||
|
label: label.value,
|
||||||
|
name: selected.value.name
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<OhVueIcon name="px-check" />
|
||||||
|
添加页面
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</Transition>
|
</Transition>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full h-4" />
|
)}
|
||||||
<Item
|
</Transition>
|
||||||
name="px-add-box"
|
|
||||||
label="添加"
|
|
||||||
onClick={() => {
|
|
||||||
showEdit.value = true
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Item
|
|
||||||
name="px-headset"
|
|
||||||
label="反馈"
|
|
||||||
onClick={() => {
|
|
||||||
router.path = 'settings-fallback'
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
class="w-[56px] h-[56px] bg-white/40 rounded-full border-white border-[2px] border-solid overflow-hidden cursor-pointer"
|
|
||||||
onClick={() => {
|
|
||||||
router.path = 'settings-user'
|
|
||||||
}}
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
{/* 添加页面 */}
|
|
||||||
<Transition name="page-adder">
|
|
||||||
{showEdit.value && (
|
|
||||||
<div
|
|
||||||
class="absolute left-[70px] bottom-0 w-56 rounded-lg p-4 bg-white/40 backdrop-blur shadow-lg"
|
|
||||||
v-outside-click={() => {
|
|
||||||
showEdit.value = false
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
class="rounded bg-black/10 text-center text-sm w-full py-1"
|
|
||||||
v-model={label.value}
|
|
||||||
maxlength={2}
|
|
||||||
/>
|
|
||||||
<div class="flex flex-wrap gap-1 mt-2">
|
|
||||||
{icons.map((el) => (
|
|
||||||
<div
|
|
||||||
class={
|
|
||||||
'p-1 rounded cursor-pointer transition-all ' +
|
|
||||||
(selected.value.name === el.name
|
|
||||||
? 'text-black/80 bg-white shadow'
|
|
||||||
: 'text-black/60 hover:shadow hover:text-black/80 hover:bg-white')
|
|
||||||
}
|
|
||||||
onClick={() => {
|
|
||||||
selected.value = { ...el }
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<OhVueIcon name={el.name} fill="black" scale={1.3} />
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="w-full mt-2 py-1 rounded-lg bg-white text-center text-sm shadow hover:shadow-lg transition-all cursor-pointer"
|
|
||||||
onClick={() => {
|
|
||||||
layout.state.content[layout.state.current].push({
|
|
||||||
list: [],
|
|
||||||
label: label.value,
|
|
||||||
name: selected.value.name
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<OhVueIcon name="px-check" />
|
|
||||||
添加页面
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Transition>
|
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import type { Layout } from './layout.types'
|
import type { Layout } from './layout.types'
|
||||||
import { reactive, ref, toRaw, watch } from 'vue'
|
import { computed, reactive, ref, toRaw, watch } from 'vue'
|
||||||
import db from '@/db'
|
import db from '@/db'
|
||||||
|
|
||||||
const defaultLayout: Layout = {
|
const defaultLayout: Layout = {
|
||||||
|
@ -9,8 +9,7 @@ const defaultLayout: Layout = {
|
||||||
currentPage: 0,
|
currentPage: 0,
|
||||||
dir: {},
|
dir: {},
|
||||||
dock: [null, null, null, null, null, null, null, null, null, null],
|
dock: [null, null, null, null, null, null, null, null, null, null],
|
||||||
simple: false,
|
simple: false
|
||||||
loading: true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineStore('layout', () => {
|
export default defineStore('layout', () => {
|
||||||
|
@ -36,8 +35,15 @@ export default defineStore('layout', () => {
|
||||||
state.currentPage = 0
|
state.currentPage = 0
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
const currentPageList = computed(() =>
|
||||||
|
state.simple ? [] : state.content[state.current]?.[state.currentPage]?.list || []
|
||||||
|
)
|
||||||
|
// 是让时间和搜索改变位置,使画面更紧凑 —— @/layout/grid
|
||||||
|
const isCompact = ref(false)
|
||||||
return {
|
return {
|
||||||
state,
|
state,
|
||||||
ready
|
ready,
|
||||||
|
currentPageList,
|
||||||
|
isCompact
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -18,7 +18,7 @@ export default defineStore(
|
||||||
blockSize: 6,
|
blockSize: 6,
|
||||||
blockPadding: 1,
|
blockPadding: 1,
|
||||||
mainWidth: 70,
|
mainWidth: 70,
|
||||||
blockRadius: 1,
|
blockRadius: 0.2,
|
||||||
// 搜索
|
// 搜索
|
||||||
searchWidth: 30,
|
searchWidth: 30,
|
||||||
searchRadius: 24
|
searchRadius: 24
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
import { defineComponent, reactive, Transition } from 'vue'
|
import { defineComponent, reactive, Transition } from 'vue'
|
||||||
import useRouterStore from '@/useRouterStore'
|
import useRouterStore from '@/useRouterStore'
|
||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
|
import useUserStore from './useUserStore'
|
||||||
|
|
||||||
export default defineComponent(() => {
|
export default defineComponent(() => {
|
||||||
const router = useRouterStore()
|
const router = useRouterStore()
|
||||||
|
const user = useUserStore()
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
email: '',
|
email: '',
|
||||||
password: ''
|
password: ''
|
||||||
})
|
})
|
||||||
return () => (
|
return () => (
|
||||||
<div class="fixed left-0 top-0 z-20 w-full">
|
<div class="fixed left-0 top-0 z-50 w-full">
|
||||||
<Transition>
|
<Transition>
|
||||||
{router.path === 'global-login' && (
|
{router.path === 'global-login' && (
|
||||||
<div
|
<div
|
||||||
|
@ -36,10 +38,11 @@ export default defineComponent(() => {
|
||||||
<input placeholder="密码" type="password" v-model={form.password} />
|
<input placeholder="密码" type="password" v-model={form.password} />
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
request('POST', '/api/user/login', {
|
request<string>('POST', '/api/user/login', {
|
||||||
data: form
|
data: form,
|
||||||
|
returnType: 'text'
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
console.log(res)
|
user.token = res
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
|
@ -1,5 +1,43 @@
|
||||||
|
import request from '@/utils/request'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
|
import { computed, ref, watch } from 'vue'
|
||||||
|
|
||||||
|
interface UserInfo {
|
||||||
|
id: string
|
||||||
|
username: string
|
||||||
|
created: string
|
||||||
|
vipUntil: string
|
||||||
|
gender: number
|
||||||
|
birthday: string
|
||||||
|
brief: string
|
||||||
|
email: string
|
||||||
|
password: string
|
||||||
|
type: string
|
||||||
|
updated: string
|
||||||
|
avatar: string
|
||||||
|
openId: string
|
||||||
|
}
|
||||||
|
|
||||||
export default defineStore('user', () => {
|
export default defineStore('user', () => {
|
||||||
return {}
|
const token = ref(localStorage.getItem('token') || '')
|
||||||
|
watch(token, (val) => {
|
||||||
|
localStorage.setItem('token', val)
|
||||||
|
})
|
||||||
|
const profile = ref<null | UserInfo>(null)
|
||||||
|
watch(
|
||||||
|
token,
|
||||||
|
(val) => {
|
||||||
|
if (!val) return
|
||||||
|
request<UserInfo>('GET', '/api/profile').then((res) => {
|
||||||
|
profile.value = res
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
const isLogin = computed(() => !!token.value && !!profile.value)
|
||||||
|
return {
|
||||||
|
token,
|
||||||
|
profile,
|
||||||
|
isLogin
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue