Merge pull request #448 from EstrellaXD/search-component

webui: 完成搜索组件的各类功能
This commit is contained in:
Estrella Pan
2023-09-09 13:52:28 +08:00
committed by GitHub
9 changed files with 178 additions and 107 deletions

View File

@@ -14,9 +14,13 @@ logger = logging.getLogger(__name__)
class RSSAnalyser(TitleParser):
def official_title_parser(self, bangumi: Bangumi, rss: RSSItem, torrent: Torrent):
if rss.parser == "mikan":
bangumi.poster_link, bangumi.official_title = self.mikan_parser(
torrent.homepage
)
try:
bangumi.poster_link, bangumi.official_title = self.mikan_parser(
torrent.homepage
)
except AttributeError:
logger.warning("[Parser] Mikan torrent has no homepage info.")
pass
elif rss.parser == "tmdb":
tmdb_title, season, year, poster_link = self.tmdb_parser(
bangumi.official_title, bangumi.season, settings.rss_parser.language

View File

@@ -2,7 +2,7 @@ import {
Observable,
} from 'rxjs';
import type { BangumiRule } from '#/bangumi';
import type { BangumiRule, BangumiAPI } from '#/bangumi';
export const apiSearch = {
/**
@@ -18,7 +18,12 @@ export const apiSearch = {
eventSource.onmessage = ev => {
try {
const data: BangumiRule = JSON.parse(ev.data);
const apiData: BangumiAPI = JSON.parse(ev.data);
const data: BangumiRule = {
...apiData,
filter: apiData.filter.split(','),
rss_link: apiData.rss_link.split(','),
}
observer.next(data);
} catch (error) {
console.error('[/search/bangumi] Parse Error |', { keyword }, 'response:', ev.data)

View File

@@ -13,8 +13,8 @@ const message = useMessage();
const { getAll } = useBangumiStore();
const rss = ref<RSS>(rssTemplate);
const searchRule = defineModel<BangumiRule>('searchRule', { default: null });
const rule = ref<BangumiRule>(ruleTemplate);
const parserType = ['mikan', 'tmdb', 'parser'];
const window = reactive({
@@ -33,6 +33,10 @@ watch(show, (val) => {
setTimeout(() => {
window.next = false;
}, 300);
} else if (val || searchRule.value) {
window.next = true;
window.rule = true;
rule.value = searchRule.value;
}
});
@@ -59,9 +63,6 @@ async function addRss() {
window.loading = true;
const data = await apiDownload.analysis(rss.value);
window.loading = false;
const response = await apiRSS.add(rss.value);
message.success(response.msg_en);
console.log('rss', response);
rule.value = data;
window.next = true;
window.rule = true;

View File

@@ -0,0 +1,91 @@
<script lang="ts" setup>
import {ref} from 'vue';
import {vOnClickOutside} from "@vueuse/components";
defineEmits(['add-bangumi']);
const showProvider = ref(false);
const {
providers,
getProviders,
provider,
loading,
onSearch,
clearSearch,
inputValue,
bangumiList,
} = useSearchStore();
onMounted(() => {
getProviders();
});
function onSelect(site: string) {
provider.value = site;
showProvider.value = false;
}
</script>
<template>
<ab-search
v-model:inputValue="inputValue"
:provider="provider"
:loading="loading"
@search="onSearch"
@select="() => showProvider = !showProvider"
/>
<div
v-show="showProvider"
v-on-click-outside="() => showProvider = false"
abs top-84px
left-540px w-100px
rounded-12px
shadow
bg-white
z-99
overflow-hidden
>
<div
v-for="site in providers"
:key="site"
hover:bg-theme-row
is-btn
@click="() => onSelect(site)"
>
<div
text-h3
text-primary
hover:text-white
p-12px
truncate
>
{{ site }}
</div>
</div>
</div>
<div
v-on-click-outside="clearSearch"
abs top-84px left-192px z-8
>
<TransitionGroup name="result" tag="ab-bangumi-card" space-y-12px>
<!-- TODO: Transition Effect to fix. -->
<ab-bangumi-card
v-for="bangumi in bangumiList"
:key="bangumi.id"
:bangumi="bangumi"
type="search"
@click="() => $emit('add-bangumi', bangumi)"
/>
</TransitionGroup>
</div>
</template>
<style lang="scss" scoped>
.result-enter-active {
transition: all 0.3s;
}
</style>

View File

@@ -1,28 +1,25 @@
<script lang="ts" setup>
import {Down, Search} from '@icon-park/vue-next';
import { vOnClickOutside } from '@vueuse/components'
import { NSpin } from 'naive-ui';
import { watch } from 'vue';
defineEmits(['click']);
withDefaults(
defineProps<{
provider: string;
loading: boolean;
}>(),
{
provider: '',
loading: false,
}
);
const {
onSelect,
onSearch,
inputValue,
selectingProvider,
provider,
providers,
getProviders,
bangumiList
} = useSearchStore();
defineEmits(['select', 'search']);
function closeModal() {
selectingProvider.value = false;
bangumiList.value = [];
}
const inputValue = defineModel<string>('inputValue');
onMounted(() => {
getProviders();
watch(inputValue, (val) => {
console.log(val);
});
</script>
@@ -41,86 +38,47 @@ onMounted(() => {
shadow-inner
>
<Search
v-if="!loading"
theme="outline"
size="24"
fill="#fff"
is-btn
btn-click
@click="onSearch"
@click="$emit('search')"
/>
<NSpin v-else :size="20"/>
<input
v-model="inputValue"
type="text"
:placeholder="$t('topbar.search.placeholder')"
input-reset
@keyup.enter="onSearch"
@keyup.enter="$emit('search')"
/>
<div
h-full
f-cer
justify-between
px-12px
w-100px
is-btn
class="provider-select"
@click="() => selectingProvider = !selectingProvider"
>
<div text-h3 truncate>
{{ provider }}
</div>
<div class="provider-select">
<Down/>
</div>
</div>
</div>
<div
v-show="selectingProvider"
v-on-click-outside="closeModal"
abs top-84px
left-540px w-100px
rounded-12px shadow bg-white z-99 overflow-hidden>
<div
v-for="site in providers"
:key="site"
hover:bg-theme-row
is-btn
@click="() => onSelect(site)"
>
<div
text-h3
text-primary
hover:text-white
p-12px
truncate
h-full
f-cer
justify-between
px-12px
w-100px
class="provider"
is-btn
@click="$emit('select')"
>
{{ site }}
<div text-h3 truncate>
{{ provider }}
</div>
<div class="provider">
<Down/>
</div>
</div>
</div>
</div>
<div
abs top-84px left-200px space-y-8px z-98
>
<ab-bangumi-card
v-for="(item, index) in bangumiList"
:key="index"
v-on-click-outside="closeModal"
:bangumi="item"
type="search"
transition-opacity
@click="$emit('click', item)"
/>
</div>
</template>
<style lang="scss" scoped>
.provider-select {
.provider {
background: #4E2A94;
}
.list-enter-active, .list-leave-active {
transition: opacity 0.5s ease;
}
</style>

View File

@@ -7,12 +7,14 @@ import {
Power,
Refresh,
} from '@icon-park/vue-next';
import type {BangumiRule} from "#/bangumi";
const {t, changeLocale} = useMyI18n();
const {running, onUpdate, offUpdate} = useAppInfo();
const show = ref(false);
const showAccount = ref(false);
const showAdd = ref(false);
const searchRule = ref<BangumiRule>()
const {start, pause, shutdown, restart, resetRule} = useProgramStore();
@@ -52,13 +54,19 @@ const items = [
label: () => t('topbar.profile.title'),
icon: Me,
handle: () => {
show.value = true;
showAccount.value = true;
},
},
];
const onSearchFocus = ref(false);
function addSearchResult(bangumi: BangumiRule) {
showAdd.value = true;
searchRule.value = bangumi;
console.log('searchRule', searchRule.value);
}
onBeforeMount(() => {
onUpdate();
});
@@ -76,7 +84,7 @@ onUnmounted(() => {
<img v-show="onSearchFocus === false" src="/images/AutoBangumi.svg" alt="AutoBangumi" h-24px rel top-2px/>
</div>
<ab-search/>
<ab-search-bar @add-bangumi="addSearchResult"/>
</div>
<div ml-auto>
@@ -88,8 +96,8 @@ onUnmounted(() => {
></ab-status-bar>
</div>
<ab-change-account v-model:show="show"></ab-change-account>
<ab-change-account v-model:show="showAccount"></ab-change-account>
<ab-add-bangumi v-model:show="showAdd"></ab-add-bangumi>
<ab-add-bangumi v-model:show="showAdd" v-model:searchRule="searchRule"></ab-add-bangumi>
</div>
</template>

View File

@@ -14,8 +14,11 @@ definePage({
<template>
<div overflow-auto mt-12px flex-grow>
<div flex="~ wrap" gap-y-12px gap-x-32px>
<transition-group name="fade">
<div>
<TransitionGroup
name="bangumi" tag="div"
flex="~ wrap" gap-y-12px gap-x-32px>
<!-- TODO: Transition Effect to fix. -->
<ab-bangumi-card
v-for="i in bangumi"
:key="i.id"
@@ -24,7 +27,7 @@ definePage({
type="primary"
@click="() => openEditPopup(i)"
></ab-bangumi-card>
</transition-group>
</TransitionGroup>
<ab-edit-rule
v-model:show="editRule.show"
@@ -40,7 +43,7 @@ definePage({
</template>
<style>
.fade-enter-active, .fade-leave-active {
.bangumi-enter-active, .bangumi-leave-active {
transition: opacity .3s;
}
</style>

View File

@@ -3,8 +3,7 @@ import {
EMPTY,
Subject,
debounceTime,
switchMap,
tap,
switchMap, tap,
} from "rxjs";
import type {BangumiRule} from "#/bangumi";
@@ -12,15 +11,17 @@ import type {BangumiRule} from "#/bangumi";
export function useSearchStore() {
const bangumiList = ref<BangumiRule[]>([]);
const inputValue = ref<string>('');
const selectingProvider = ref<boolean>(false);
const providers = ref<string[]>(['mikan', 'dmhy', 'nyaa']);
const provider = ref<string>('mikan');
const provider = ref<string>(providers.value[0]);
const loading = ref<boolean>(false);
const input$ = new Subject<string>();
watch(inputValue, input => {
input$.next(input);
loading.value = !!input;
})
const {execute: getProviders, onResult: onGetProvidersResult} = useApi(
@@ -44,8 +45,8 @@ export function useSearchStore() {
// 有输入更新后清理之前的搜索结果
bangumiList.value = [];
return input
? apiSearch.get(input, provider.value)
: EMPTY
? apiSearch.get(input, provider.value)
: EMPTY
}),
tap((bangumi: BangumiRule) => {
bangumiList.value.push(bangumi);
@@ -56,19 +57,18 @@ export function useSearchStore() {
input$.next(inputValue.value);
}
function onSelect(site: string) {
provider.value = site;
selectingProvider.value = !selectingProvider.value
onSearch();
function clearSearch() {
inputValue.value = '';
bangumiList.value = [];
}
return {
input$,
bangumiInfo$,
inputValue,
selectingProvider,
onSelect,
onSearch,
clearSearch,
loading,
provider,
getProviders,
providers,

View File

@@ -24,6 +24,7 @@ declare module '@vue/runtime-core' {
AbRssItem: typeof import('./../../src/components/ab-rss-item.vue')['default']
AbRule: typeof import('./../../src/components/ab-rule.vue')['default']
AbSearch: typeof import('./../../src/components/basic/ab-search.vue')['default']
AbSearchBar: typeof import('./../../src/components/ab-search-bar.vue')['default']
AbSelect: typeof import('./../../src/components/basic/ab-select.vue')['default']
AbSetting: typeof import('./../../src/components/ab-setting.vue')['default']
AbSidebar: typeof import('./../../src/components/layout/ab-sidebar.vue')['default']