89 lines
2.5 KiB
Vue
89 lines
2.5 KiB
Vue
<script setup lang="ts">
|
|||
|
|
import { ref, computed, onMounted, watch } from 'vue'
|
||
|
|
import { useRoute, useRouter } from 'vue-router'
|
||
|
|
import ClientHeader from '../components/client/ClientHeader.vue'
|
||
|
|
import QrCodeModal from '../components/QrCodeModal.vue'
|
||
|
|
import ConfirmModal from '../components/ConfirmModal.vue'
|
||
|
|
import { useLibraryRoots } from '../composables/useLibraryRoots'
|
||
|
|
import { useMediaPlayer } from '../composables/useMediaPlayer'
|
||
|
|
import { useModals } from '../composables/useModals'
|
||
|
|
|
||
|
|
const route = useRoute()
|
||
|
|
const router = useRouter()
|
||
|
|
const { activeRoots, loadRoots } = useLibraryRoots()
|
||
|
|
const { selectedFile, flushVideoProgress } = useMediaPlayer()
|
||
|
|
const { confirmModal, qrModal } = useModals()
|
||
|
|
|
||
|
|
const searchQuery = ref('')
|
||
|
|
const errorMessage = ref('')
|
||
|
|
|
||
|
|
const isSearching = computed(() => route.name === 'search')
|
||
|
|
const isBrowsingRoots = computed(() => {
|
||
|
|
const name = route.name as string
|
||
|
|
return name === 'home' || name === 'recent-added' || name === 'recent-played'
|
||
|
|
})
|
||
|
|
|
||
|
|
const activeTab = computed<'recent-added' | 'recent-played' | 'libraries'>(() => {
|
||
|
|
const name = route.name as string
|
||
|
|
if (name === 'recent-added') return 'recent-added'
|
||
|
|
if (name === 'recent-played') return 'recent-played'
|
||
|
|
return 'libraries'
|
||
|
|
})
|
||
|
|
|
||
|
|
const clientTitle = computed(() => {
|
||
|
|
if (activeTab.value === 'recent-added') return '最近添加'
|
||
|
|
if (activeTab.value === 'recent-played') return '最近播放'
|
||
|
|
if (isBrowsingRoots.value) return '文件库'
|
||
|
|
return '文件'
|
||
|
|
})
|
||
|
|
|
||
|
|
async function doSearch() {
|
||
|
|
const keyword = searchQuery.value.trim()
|
||
|
|
if (!keyword) return
|
||
|
|
flushVideoProgress()
|
||
|
|
router.push({ name: 'search', query: { q: keyword } })
|
||
|
|
}
|
||
|
|
|
||
|
|
function backToRoots() {
|
||
|
|
flushVideoProgress()
|
||
|
|
router.push('/')
|
||
|
|
}
|
||
|
|
|
||
|
|
function exitSearch() {
|
||
|
|
flushVideoProgress()
|
||
|
|
searchQuery.value = ''
|
||
|
|
router.push('/')
|
||
|
|
}
|
||
|
|
|
||
|
|
onMounted(() => {
|
||
|
|
window.addEventListener('beforeunload', flushVideoProgress)
|
||
|
|
if (activeRoots.value.length === 0) loadRoots()
|
||
|
|
})
|
||
|
|
</script>
|
||
|
|
|
||
|
|
<template>
|
||
|
|
<main class="client-shell">
|
||
|
|
<ClientHeader
|
||
|
|
v-model:search-query="searchQuery"
|
||
|
|
:title="clientTitle"
|
||
|
|
:active-tab="activeTab"
|
||
|
|
:recent-count="0"
|
||
|
|
:active-roots-count="activeRoots.length"
|
||
|
|
:is-browsing-roots="isBrowsingRoots"
|
||
|
|
:is-searching="isSearching"
|
||
|
|
:browse-data="null"
|
||
|
|
@search="doSearch"
|
||
|
|
@back="backToRoots"
|
||
|
|
@exit-search="exitSearch"
|
||
|
|
@open-qr="qrModal?.open()"
|
||
|
|
/>
|
||
|
|
|
||
|
|
<p v-if="errorMessage" class="error-banner">{{ errorMessage }}</p>
|
||
|
|
|
||
|
|
<router-view />
|
||
|
|
</main>
|
||
|
|
|
||
|
|
<QrCodeModal ref="qrModal" />
|
||
|
|
<ConfirmModal ref="confirmModal" />
|
||
|
|
</template>
|