mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-06-09 09:40:14 +08:00
feat:聚合搜索(working...)
This commit is contained in:
@@ -19,7 +19,6 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@bytebase/vue-kbar": "^0.1.8",
|
|
||||||
"@fullcalendar/core": "^6.1.8",
|
"@fullcalendar/core": "^6.1.8",
|
||||||
"@fullcalendar/daygrid": "^6.1.8",
|
"@fullcalendar/daygrid": "^6.1.8",
|
||||||
"@fullcalendar/interaction": "^6.1.7",
|
"@fullcalendar/interaction": "^6.1.7",
|
||||||
|
|||||||
@@ -14,7 +14,10 @@ function onClick() {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<IconBtn :class="props.innerClass ? props.innerClass : 'absolute right-3 top-3'" @click.stop="onClick">
|
<IconBtn
|
||||||
|
:class="props.innerClass ? props.innerClass : 'absolute right-3 top-3'"
|
||||||
|
@click.stop="onClick"
|
||||||
|
>
|
||||||
<VIcon icon="mdi-close" />
|
<VIcon icon="mdi-close" />
|
||||||
</IconBtn>
|
</IconBtn>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
import VerticalNavSectionTitle from '@/@layouts/components/VerticalNavSectionTitle.vue'
|
import VerticalNavSectionTitle from '@/@layouts/components/VerticalNavSectionTitle.vue'
|
||||||
import VerticalNavLayout from '@layouts/components/VerticalNavLayout.vue'
|
import VerticalNavLayout from '@layouts/components/VerticalNavLayout.vue'
|
||||||
import VerticalNavLink from '@layouts/components/VerticalNavLink.vue'
|
import VerticalNavLink from '@layouts/components/VerticalNavLink.vue'
|
||||||
|
|
||||||
// Components
|
|
||||||
import Footer from '@/layouts/components/Footer.vue'
|
import Footer from '@/layouts/components/Footer.vue'
|
||||||
import NavbarThemeSwitcher from '@/layouts/components/NavbarThemeSwitcher.vue'
|
import NavbarThemeSwitcher from '@/layouts/components/NavbarThemeSwitcher.vue'
|
||||||
import UserNofification from '@/layouts/components/UserNotification.vue'
|
import UserNofification from '@/layouts/components/UserNotification.vue'
|
||||||
@@ -25,21 +23,16 @@ const superUser = store.state.auth.superUser
|
|||||||
<IconBtn class="ms-n2 d-lg-none" @click="toggleVerticalOverlayNavActive(true)">
|
<IconBtn class="ms-n2 d-lg-none" @click="toggleVerticalOverlayNavActive(true)">
|
||||||
<VIcon icon="mdi-menu" />
|
<VIcon icon="mdi-menu" />
|
||||||
</IconBtn>
|
</IconBtn>
|
||||||
|
|
||||||
<!-- 👉 Search Bar -->
|
<!-- 👉 Search Bar -->
|
||||||
<SearchBar />
|
<SearchBar />
|
||||||
|
<!-- 👉 Spacer -->
|
||||||
<VSpacer />
|
<VSpacer />
|
||||||
|
|
||||||
<!-- 👉 Shortcuts -->
|
<!-- 👉 Shortcuts -->
|
||||||
<ShortcutBar v-if="superUser" />
|
<ShortcutBar v-if="superUser" />
|
||||||
|
|
||||||
<!-- 👉 Theme -->
|
<!-- 👉 Theme -->
|
||||||
<NavbarThemeSwitcher />
|
<NavbarThemeSwitcher />
|
||||||
|
|
||||||
<!-- 👉 Notification -->
|
<!-- 👉 Notification -->
|
||||||
<UserNofification />
|
<UserNofification />
|
||||||
|
|
||||||
<!-- 👉 UserProfile -->
|
<!-- 👉 UserProfile -->
|
||||||
<UserProfile />
|
<UserProfile />
|
||||||
</div>
|
</div>
|
||||||
@@ -53,7 +46,6 @@ const superUser = store.state.auth.superUser
|
|||||||
to: '/dashboard',
|
to: '/dashboard',
|
||||||
}"
|
}"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- 👉 发现 -->
|
<!-- 👉 发现 -->
|
||||||
<VerticalNavSectionTitle
|
<VerticalNavSectionTitle
|
||||||
:item="{
|
:item="{
|
||||||
@@ -74,7 +66,6 @@ const superUser = store.state.auth.superUser
|
|||||||
to: '/resource',
|
to: '/resource',
|
||||||
}"
|
}"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- 👉 订阅 -->
|
<!-- 👉 订阅 -->
|
||||||
<VerticalNavSectionTitle
|
<VerticalNavSectionTitle
|
||||||
:item="{
|
:item="{
|
||||||
@@ -131,7 +122,6 @@ const superUser = store.state.auth.superUser
|
|||||||
to: '/filemanager',
|
to: '/filemanager',
|
||||||
}"
|
}"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- 👉 系统 -->
|
<!-- 👉 系统 -->
|
||||||
<VerticalNavSectionTitle
|
<VerticalNavSectionTitle
|
||||||
v-if="superUser"
|
v-if="superUser"
|
||||||
@@ -166,10 +156,8 @@ const superUser = store.state.auth.superUser
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #after-vertical-nav-items />
|
<template #after-vertical-nav-items />
|
||||||
|
|
||||||
<!-- 👉 Pages -->
|
<!-- 👉 Pages -->
|
||||||
<slot />
|
<slot />
|
||||||
|
|
||||||
<!-- 👉 Footer -->
|
<!-- 👉 Footer -->
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<Footer />
|
<Footer />
|
||||||
|
|||||||
@@ -1,109 +1,33 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
// 路由
|
import SearchBarView from '@/views/system/SearchBarView.vue'
|
||||||
const router = useRouter()
|
|
||||||
|
|
||||||
// 搜索词
|
|
||||||
const searchWord = ref(null)
|
|
||||||
|
|
||||||
// 搜索弹窗
|
|
||||||
const searchDialog = ref(false)
|
const searchDialog = ref(false)
|
||||||
|
|
||||||
// ref
|
|
||||||
const searchWordInput = ref<HTMLElement | null>(null)
|
|
||||||
|
|
||||||
// 当前的搜索类型 media/person
|
|
||||||
const searchType = ref('media')
|
|
||||||
|
|
||||||
// 搜索提示词列表
|
|
||||||
const searchHintList = ref<string[]>([])
|
|
||||||
|
|
||||||
// Search
|
|
||||||
function search() {
|
|
||||||
if (!searchWord.value) return
|
|
||||||
if (!searchHintList.value.includes(searchWord.value)) searchHintList.value.push(searchWord.value)
|
|
||||||
searchDialog.value = false
|
|
||||||
router.push({
|
|
||||||
path: '/browse/media/search',
|
|
||||||
query: {
|
|
||||||
title: searchWord.value,
|
|
||||||
type: searchType.value,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 切换搜索类型
|
|
||||||
function switchSearchType() {
|
|
||||||
searchType.value = searchType.value === 'media' ? 'person' : 'media'
|
|
||||||
}
|
|
||||||
|
|
||||||
// 打开搜索弹窗
|
|
||||||
function openSearchDialog() {
|
function openSearchDialog() {
|
||||||
searchDialog.value = true
|
searchDialog.value = true
|
||||||
nextTick(() => {
|
|
||||||
searchWordInput.value?.focus()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<!-- 👉 Search Button -->
|
|
||||||
<div class="d-flex align-center cursor-pointer" style="user-select: none">
|
|
||||||
<VDialog v-model="searchDialog" max-width="50rem" transition="dialog-top-transition">
|
|
||||||
<!-- Dialog Content -->
|
|
||||||
<VCard title="搜索">
|
|
||||||
<VCardText>
|
|
||||||
<VRow>
|
|
||||||
<VCol cols="12">
|
|
||||||
<VCombobox
|
|
||||||
ref="searchWordInput"
|
|
||||||
v-model="searchWord"
|
|
||||||
:items="searchHintList"
|
|
||||||
:prepend-inner-icon="searchType == 'person' ? 'mdi-account' : 'mdi-movie'"
|
|
||||||
:label="searchType == 'person' ? '搜索演员' : '搜索电影、电视剧'"
|
|
||||||
@keydown.enter="search"
|
|
||||||
@click:prepend-inner="switchSearchType"
|
|
||||||
clearable
|
|
||||||
/>
|
|
||||||
</VCol>
|
|
||||||
</VRow>
|
|
||||||
</VCardText>
|
|
||||||
|
|
||||||
<VCardActions>
|
|
||||||
<VSpacer />
|
|
||||||
<VBtn variant="tonal" @click="search"> 搜索 </VBtn>
|
|
||||||
</VCardActions>
|
|
||||||
</VCard>
|
|
||||||
</VDialog>
|
|
||||||
</div>
|
|
||||||
<!-- 👉 Search Icon -->
|
<!-- 👉 Search Icon -->
|
||||||
<IconBtn class="d-md-none" @click="openSearchDialog">
|
<div class="d-flex align-center cursor-pointer ms-lg-n2" style="user-select: none">
|
||||||
<VIcon icon="mdi-magnify" />
|
<IconBtn @click="openSearchDialog">
|
||||||
</IconBtn>
|
<VIcon icon="ri-search-line" />
|
||||||
<!-- 👉 Search Textfield -->
|
</IconBtn>
|
||||||
<span class="w-full me-3">
|
<span class="d-none d-md-flex align-center text-disabled ms-2" @click="openSearchDialog">
|
||||||
<VCombobox
|
<span class="me-3">搜索</span>
|
||||||
key="search_navbar"
|
<span class="meta-key">⌘K</span>
|
||||||
v-model="searchWord"
|
</span>
|
||||||
:items="searchHintList"
|
</div>
|
||||||
class="d-none d-md-block text-disabled search-box"
|
<!-- 搜索弹窗 -->
|
||||||
density="compact"
|
<SearchBarView v-model="searchDialog" @close="searchDialog = false" />
|
||||||
variant="solo"
|
|
||||||
:prepend-inner-icon="searchType == 'person' ? 'mdi-account' : 'mdi-movie'"
|
|
||||||
:label="searchType == 'person' ? '搜索演员' : '搜索电影、电视剧'"
|
|
||||||
append-inner-icon="mdi-magnify"
|
|
||||||
single-line
|
|
||||||
hide-details
|
|
||||||
flat
|
|
||||||
rounded
|
|
||||||
@click:append-inner="search"
|
|
||||||
@click:prepend-inner="switchSearchType"
|
|
||||||
@keydown.enter="search"
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</template>
|
</template>
|
||||||
|
<style type="scss" scoped>
|
||||||
<style lang="scss">
|
.meta-key {
|
||||||
.search-box div.v-input__control div[role='textbox'] {
|
border: thin solid rgba(var(--v-border-color), var(--v-border-opacity));
|
||||||
border: 1px solid rgb(var(--v-theme-background));
|
border-radius: 6px;
|
||||||
|
block-size: 1.75rem;
|
||||||
|
padding-block: 0.1rem;
|
||||||
|
padding-inline: 0.25rem;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
107
src/views/system/SearchBarView.vue
Normal file
107
src/views/system/SearchBarView.vue
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
// 路由
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
// 定义事件
|
||||||
|
const emit = defineEmits(['close'])
|
||||||
|
|
||||||
|
// 搜索词
|
||||||
|
const searchWord = ref(null)
|
||||||
|
|
||||||
|
// ref
|
||||||
|
const searchWordInput = ref<HTMLElement | null>(null)
|
||||||
|
|
||||||
|
// 搜索提示词列表
|
||||||
|
const searchHintList = ref<string[]>([])
|
||||||
|
|
||||||
|
// Search
|
||||||
|
function search(searchType: string) {
|
||||||
|
// 搜索类型 media/person
|
||||||
|
if (!searchWord.value) return
|
||||||
|
if (!searchHintList.value.includes(searchWord.value)) searchHintList.value.push(searchWord.value)
|
||||||
|
router.push({
|
||||||
|
path: '/browse/media/search',
|
||||||
|
query: {
|
||||||
|
title: searchWord.value,
|
||||||
|
type: searchType,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
emit('close')
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
searchWordInput.value?.focus()
|
||||||
|
}, 500)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<VDialog max-width="40rem">
|
||||||
|
<VCard>
|
||||||
|
<VCardText class="pe-12">
|
||||||
|
<VCombobox
|
||||||
|
ref="searchWordInput"
|
||||||
|
v-model="searchWord"
|
||||||
|
density="compact"
|
||||||
|
variant="plain"
|
||||||
|
class="text-high-emphasis"
|
||||||
|
placeholder="搜索 ..."
|
||||||
|
:items="searchHintList"
|
||||||
|
>
|
||||||
|
<template #prepend>
|
||||||
|
<VIcon icon="ri-search-line" style="opacity: 1" />
|
||||||
|
</template>
|
||||||
|
</VCombobox>
|
||||||
|
</VCardText>
|
||||||
|
<DialogCloseBtn inner-class="absolute right-3 top-5 text-high-emphasis" @click="emit('close')" />
|
||||||
|
<VDivider />
|
||||||
|
<div class="ps h-100">
|
||||||
|
<VList lines="one" v-if="searchWord">
|
||||||
|
<!-- 搜索结果 -->
|
||||||
|
<VListSubheader v-if="searchWord"> 媒体 </VListSubheader>
|
||||||
|
<VHover>
|
||||||
|
<template #default="hover">
|
||||||
|
<VListItem
|
||||||
|
prepend-icon="mdi-movie-search"
|
||||||
|
density="compact"
|
||||||
|
link
|
||||||
|
v-bind="hover.props"
|
||||||
|
@click="search('media')"
|
||||||
|
>
|
||||||
|
<VListItemTitle>
|
||||||
|
搜索 <span class="font-bold">{{ searchWord }} </span> 相关的电影、电视剧
|
||||||
|
</VListItemTitle>
|
||||||
|
<template #append>
|
||||||
|
<VIcon v-if="hover.isHovering" icon="ri-corner-down-left-line" />
|
||||||
|
</template>
|
||||||
|
</VListItem>
|
||||||
|
</template>
|
||||||
|
</VHover>
|
||||||
|
<VHover>
|
||||||
|
<template #default="hover">
|
||||||
|
<VListItem
|
||||||
|
prepend-icon="mdi-people"
|
||||||
|
density="compact"
|
||||||
|
link
|
||||||
|
v-bind="hover.props"
|
||||||
|
@click="search('person')"
|
||||||
|
>
|
||||||
|
<VListItemTitle>
|
||||||
|
搜索 <span class="font-bold">{{ searchWord }}</span> 相关的人物
|
||||||
|
</VListItemTitle>
|
||||||
|
<template #append>
|
||||||
|
<VIcon v-if="hover.isHovering" icon="ri-corner-down-left-line" />
|
||||||
|
</template>
|
||||||
|
</VListItem>
|
||||||
|
</template>
|
||||||
|
</VHover>
|
||||||
|
<VListSubheader v-if="searchWord"> 功能 </VListSubheader>
|
||||||
|
<VListSubheader v-if="searchWord"> 插件 </VListSubheader>
|
||||||
|
</VList>
|
||||||
|
<div v-else>
|
||||||
|
<!-- 默认 -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</VCard>
|
||||||
|
</VDialog>
|
||||||
|
</template>
|
||||||
Reference in New Issue
Block a user