mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-09-05 15:36:49 +08:00
添加国际化支持:在多个页面和组件中引入 vue-i18n
This commit is contained in:
Vendored
+2
@@ -198,6 +198,7 @@ declare global {
|
|||||||
const useFullscreen: typeof import('@vueuse/core')['useFullscreen']
|
const useFullscreen: typeof import('@vueuse/core')['useFullscreen']
|
||||||
const useGamepad: typeof import('@vueuse/core')['useGamepad']
|
const useGamepad: typeof import('@vueuse/core')['useGamepad']
|
||||||
const useGeolocation: typeof import('@vueuse/core')['useGeolocation']
|
const useGeolocation: typeof import('@vueuse/core')['useGeolocation']
|
||||||
|
const useI18n: typeof import('vue-i18n')['useI18n']
|
||||||
const useId: typeof import('vue')['useId']
|
const useId: typeof import('vue')['useId']
|
||||||
const useIdle: typeof import('@vueuse/core')['useIdle']
|
const useIdle: typeof import('@vueuse/core')['useIdle']
|
||||||
const useImage: typeof import('@vueuse/core')['useImage']
|
const useImage: typeof import('@vueuse/core')['useImage']
|
||||||
@@ -526,6 +527,7 @@ declare module 'vue' {
|
|||||||
readonly useFullscreen: UnwrapRef<typeof import('@vueuse/core')['useFullscreen']>
|
readonly useFullscreen: UnwrapRef<typeof import('@vueuse/core')['useFullscreen']>
|
||||||
readonly useGamepad: UnwrapRef<typeof import('@vueuse/core')['useGamepad']>
|
readonly useGamepad: UnwrapRef<typeof import('@vueuse/core')['useGamepad']>
|
||||||
readonly useGeolocation: UnwrapRef<typeof import('@vueuse/core')['useGeolocation']>
|
readonly useGeolocation: UnwrapRef<typeof import('@vueuse/core')['useGeolocation']>
|
||||||
|
readonly useI18n: UnwrapRef<typeof import('vue-i18n')['useI18n']>
|
||||||
readonly useId: UnwrapRef<typeof import('vue')['useId']>
|
readonly useId: UnwrapRef<typeof import('vue')['useId']>
|
||||||
readonly useIdle: UnwrapRef<typeof import('@vueuse/core')['useIdle']>
|
readonly useIdle: UnwrapRef<typeof import('@vueuse/core')['useIdle']>
|
||||||
readonly useImage: UnwrapRef<typeof import('@vueuse/core')['useImage']>
|
readonly useImage: UnwrapRef<typeof import('@vueuse/core')['useImage']>
|
||||||
|
|||||||
+80
-2
@@ -14,7 +14,7 @@ export default {
|
|||||||
inputMessage: 'Enter message or command',
|
inputMessage: 'Enter message or command',
|
||||||
send: 'Send',
|
send: 'Send',
|
||||||
noData: 'No Data',
|
noData: 'No Data',
|
||||||
noContent: 'No content found',
|
noContent: 'No Content Found',
|
||||||
},
|
},
|
||||||
theme: {
|
theme: {
|
||||||
light: 'Light',
|
light: 'Light',
|
||||||
@@ -22,7 +22,7 @@ export default {
|
|||||||
auto: 'Auto',
|
auto: 'Auto',
|
||||||
transparent: 'Transparent',
|
transparent: 'Transparent',
|
||||||
purple: 'Purple',
|
purple: 'Purple',
|
||||||
custom: 'Custom',
|
custom: 'Custom Theme',
|
||||||
},
|
},
|
||||||
app: {
|
app: {
|
||||||
moviepilot: 'MoviePilot',
|
moviepilot: 'MoviePilot',
|
||||||
@@ -228,5 +228,83 @@ export default {
|
|||||||
addFailed: 'Failed to add {name}: {message}!',
|
addFailed: 'Failed to add {name}: {message}!',
|
||||||
cancelSuccess: 'Subscription cancelled!',
|
cancelSuccess: 'Subscription cancelled!',
|
||||||
cancelFailed: 'Failed to cancel subscription: {message}!',
|
cancelFailed: 'Failed to cancel subscription: {message}!',
|
||||||
|
filterSubscriptions: 'Filter Subscriptions',
|
||||||
|
name: 'Name',
|
||||||
|
searchShares: 'Search Subscription Shares',
|
||||||
|
keyword: 'Keyword',
|
||||||
|
},
|
||||||
|
recommend: {
|
||||||
|
all: 'All',
|
||||||
|
categoryMovie: 'Movies',
|
||||||
|
categoryTV: 'TV Shows',
|
||||||
|
categoryAnime: 'Anime',
|
||||||
|
categoryRankings: 'Rankings',
|
||||||
|
trendingNow: 'Trending Now',
|
||||||
|
nowShowing: 'Now Showing',
|
||||||
|
bangumiDaily: 'Bangumi Daily',
|
||||||
|
tmdbHotMovies: 'TMDB Hot Movies',
|
||||||
|
tmdbHotTVShows: 'TMDB Hot TV Shows',
|
||||||
|
doubanHotMovies: 'Douban Hot Movies',
|
||||||
|
doubanHotTVShows: 'Douban Hot TV Shows',
|
||||||
|
doubanHotAnime: 'Douban Hot Anime',
|
||||||
|
doubanNewMovies: 'Douban New Movies',
|
||||||
|
doubanNewTVShows: 'Douban New TV Shows',
|
||||||
|
doubanTop250: 'Douban Movie TOP250',
|
||||||
|
doubanChineseTVRankings: 'Douban Chinese TV Rankings',
|
||||||
|
doubanGlobalTVRankings: 'Douban Global TV Rankings',
|
||||||
|
noCategoryContent: 'No content to display in this category',
|
||||||
|
configureContent: 'Configure Content',
|
||||||
|
customizeContent: 'Customize Content',
|
||||||
|
selectContentToDisplay: 'Select the content you want to display',
|
||||||
|
selectAll: 'Select All',
|
||||||
|
selectNone: 'Select None',
|
||||||
|
},
|
||||||
|
discover: {
|
||||||
|
setTabOrder: 'Set Tab Order',
|
||||||
|
dragToReorder: 'Drag to reorder tabs',
|
||||||
|
},
|
||||||
|
downloading: {
|
||||||
|
noDownloader: 'No Downloaders',
|
||||||
|
configureDownloader: 'Please configure and enable downloaders in settings first.',
|
||||||
|
},
|
||||||
|
resource: {
|
||||||
|
searchResults: 'Resource Search Results',
|
||||||
|
keyword: 'Keyword',
|
||||||
|
title: 'Title',
|
||||||
|
year: 'Year',
|
||||||
|
season: 'Season',
|
||||||
|
switchingView: 'Switching View',
|
||||||
|
backToHome: 'Back to Home',
|
||||||
|
searching: 'Searching, please wait...',
|
||||||
|
noData: 'No Data',
|
||||||
|
noResourceFound: 'No resources found',
|
||||||
|
},
|
||||||
|
browse: {
|
||||||
|
actor: 'Actor',
|
||||||
|
},
|
||||||
|
appcenter: {
|
||||||
|
others: 'Others',
|
||||||
|
},
|
||||||
|
notFound: {
|
||||||
|
title: 'Page Not Found ⚠️',
|
||||||
|
description: 'The page you are trying to access does not exist. Please check the URL.',
|
||||||
|
backButton: 'Back',
|
||||||
|
},
|
||||||
|
torrent: {
|
||||||
|
sortDefault: 'Default',
|
||||||
|
sortSite: 'Site',
|
||||||
|
sortSize: 'Size',
|
||||||
|
sortSeeder: 'Seeders',
|
||||||
|
filterSite: 'Site',
|
||||||
|
filterSeason: 'Season',
|
||||||
|
filterFreeState: 'Promotion',
|
||||||
|
filterVideoCode: 'Video Code',
|
||||||
|
filterEdition: 'Quality',
|
||||||
|
filterResolution: 'Resolution',
|
||||||
|
filterReleaseGroup: 'Release Group',
|
||||||
|
clearFilters: 'Clear',
|
||||||
|
selectAll: 'Select All',
|
||||||
|
loadMore: 'Load More',
|
||||||
|
noMatchingResults: 'No matching results',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -228,5 +228,83 @@ export default {
|
|||||||
addFailed: '添加{name}失败:{message}!',
|
addFailed: '添加{name}失败:{message}!',
|
||||||
cancelSuccess: '已取消订阅!',
|
cancelSuccess: '已取消订阅!',
|
||||||
cancelFailed: '取消订阅失败:{message}!',
|
cancelFailed: '取消订阅失败:{message}!',
|
||||||
|
filterSubscriptions: '筛选订阅',
|
||||||
|
name: '名称',
|
||||||
|
searchShares: '搜索订阅分享',
|
||||||
|
keyword: '关键词',
|
||||||
|
},
|
||||||
|
recommend: {
|
||||||
|
all: '全部',
|
||||||
|
categoryMovie: '电影',
|
||||||
|
categoryTV: '电视剧',
|
||||||
|
categoryAnime: '动漫',
|
||||||
|
categoryRankings: '榜单',
|
||||||
|
trendingNow: '流行趋势',
|
||||||
|
nowShowing: '正在热映',
|
||||||
|
bangumiDaily: 'Bangumi每日放送',
|
||||||
|
tmdbHotMovies: 'TMDB热门电影',
|
||||||
|
tmdbHotTVShows: 'TMDB热门电视剧',
|
||||||
|
doubanHotMovies: '豆瓣热门电影',
|
||||||
|
doubanHotTVShows: '豆瓣热门电视剧',
|
||||||
|
doubanHotAnime: '豆瓣热门动漫',
|
||||||
|
doubanNewMovies: '豆瓣最新电影',
|
||||||
|
doubanNewTVShows: '豆瓣最新电视剧',
|
||||||
|
doubanTop250: '豆瓣电影TOP250',
|
||||||
|
doubanChineseTVRankings: '豆瓣国产剧集榜',
|
||||||
|
doubanGlobalTVRankings: '豆瓣全球剧集榜',
|
||||||
|
noCategoryContent: '当前分类下没有可显示的内容',
|
||||||
|
configureContent: '设置显示内容',
|
||||||
|
customizeContent: '自定义内容',
|
||||||
|
selectContentToDisplay: '选择您想在页面显示的内容',
|
||||||
|
selectAll: '全选',
|
||||||
|
selectNone: '全不选',
|
||||||
|
},
|
||||||
|
discover: {
|
||||||
|
setTabOrder: '设置标签顺序',
|
||||||
|
dragToReorder: '拖动对标签页进行排序',
|
||||||
|
},
|
||||||
|
downloading: {
|
||||||
|
noDownloader: '没有下载器',
|
||||||
|
configureDownloader: '请先在设置中正确配置并启用下载器。',
|
||||||
|
},
|
||||||
|
resource: {
|
||||||
|
searchResults: '资源搜索结果',
|
||||||
|
keyword: '关键词',
|
||||||
|
title: '标题',
|
||||||
|
year: '年份',
|
||||||
|
season: '季',
|
||||||
|
switchingView: '切换视图',
|
||||||
|
backToHome: '返回首页',
|
||||||
|
searching: '正在搜索,请稍候...',
|
||||||
|
noData: '没有数据',
|
||||||
|
noResourceFound: '未搜索到任何资源',
|
||||||
|
},
|
||||||
|
browse: {
|
||||||
|
actor: '演员',
|
||||||
|
},
|
||||||
|
appcenter: {
|
||||||
|
others: '其他',
|
||||||
|
},
|
||||||
|
notFound: {
|
||||||
|
title: '页面不存在 ⚠️',
|
||||||
|
description: '您想要访问的页面不存在,请检查地址是否正确。',
|
||||||
|
backButton: '返回',
|
||||||
|
},
|
||||||
|
torrent: {
|
||||||
|
sortDefault: '默认',
|
||||||
|
sortSite: '站点',
|
||||||
|
sortSize: '大小',
|
||||||
|
sortSeeder: '做种数',
|
||||||
|
filterSite: '站点',
|
||||||
|
filterSeason: '季集',
|
||||||
|
filterFreeState: '促销状态',
|
||||||
|
filterVideoCode: '视频编码',
|
||||||
|
filterEdition: '质量',
|
||||||
|
filterResolution: '分辨率',
|
||||||
|
filterReleaseGroup: '制作组',
|
||||||
|
clearFilters: '清除',
|
||||||
|
selectAll: '全选',
|
||||||
|
loadMore: '加载更多',
|
||||||
|
noMatchingResults: '没有匹配的结果',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-10
@@ -1,19 +1,16 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import NoDataFound from '@/components/NoDataFound.vue'
|
import NoDataFound from '@/components/NoDataFound.vue'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
|
// 国际化
|
||||||
|
const { t } = useI18n()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NoDataFound
|
<NoDataFound error-code="404" :error-title="t('notFound.title')" :error-description="t('notFound.description')">
|
||||||
error-code="404"
|
|
||||||
error-title="页面不存在 ⚠️"
|
|
||||||
error-description="您想要访问的页面不存在,请检查地址是否正确。"
|
|
||||||
>
|
|
||||||
<template #button>
|
<template #button>
|
||||||
<VBtn
|
<VBtn to="/" class="mt-10">
|
||||||
to="/"
|
{{ t('notFound.backButton') }}
|
||||||
class="mt-10"
|
|
||||||
>
|
|
||||||
返回
|
|
||||||
</VBtn>
|
</VBtn>
|
||||||
</template>
|
</template>
|
||||||
</NoDataFound>
|
</NoDataFound>
|
||||||
|
|||||||
@@ -2,6 +2,10 @@
|
|||||||
import { NavMenu } from '@/@layouts/types'
|
import { NavMenu } from '@/@layouts/types'
|
||||||
import { getNavMenus } from '@/router/i18n-menu'
|
import { getNavMenus } from '@/router/i18n-menu'
|
||||||
import { useUserStore } from '@/stores'
|
import { useUserStore } from '@/stores'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
|
// 国际化
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
// 从 Store 中获取superuser信息
|
// 从 Store 中获取superuser信息
|
||||||
const superUser = useUserStore().superUser
|
const superUser = useUserStore().superUser
|
||||||
@@ -18,7 +22,7 @@ function categorizeApps() {
|
|||||||
const groupedMenus: Record<string, NavMenu[]> = {}
|
const groupedMenus: Record<string, NavMenu[]> = {}
|
||||||
|
|
||||||
menus.forEach(menu => {
|
menus.forEach(menu => {
|
||||||
const header = menu.header || '其他'
|
const header = menu.header || t('appcenter.others')
|
||||||
if (!groupedMenus[header]) {
|
if (!groupedMenus[header]) {
|
||||||
groupedMenus[header] = []
|
groupedMenus[header] = []
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import MediaCardListView from '@/views/discover/MediaCardListView.vue'
|
import MediaCardListView from '@/views/discover/MediaCardListView.vue'
|
||||||
import PersonCardListView from '@/views/discover/PersonCardListView.vue'
|
import PersonCardListView from '@/views/discover/PersonCardListView.vue'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
|
// 国际化
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
// 输入参数
|
// 输入参数
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -16,7 +20,7 @@ let title = route.query?.title?.toString()
|
|||||||
|
|
||||||
// 类型
|
// 类型
|
||||||
const type = route.query?.type?.toString()
|
const type = route.query?.type?.toString()
|
||||||
if (type === 'person') title = '演员:' + title
|
if (type === 'person') title = t('browse.actor') + ': ' + title
|
||||||
|
|
||||||
// 计算API路径
|
// 计算API路径
|
||||||
function getApiPath(paths: string[] | string) {
|
function getApiPath(paths: string[] | string) {
|
||||||
|
|||||||
@@ -7,6 +7,10 @@ import BangumiView from '@/views/discover/BangumiView.vue'
|
|||||||
import ExtraSourceView from '@/views/discover/ExtraSourceView.vue'
|
import ExtraSourceView from '@/views/discover/ExtraSourceView.vue'
|
||||||
import { DiscoverSource } from '@/api/types'
|
import { DiscoverSource } from '@/api/types'
|
||||||
import api from '@/api'
|
import api from '@/api'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
|
// 国际化
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
const activeTab = ref('')
|
const activeTab = ref('')
|
||||||
|
|
||||||
@@ -180,13 +184,13 @@ onActivated(async () => {
|
|||||||
<VCardItem>
|
<VCardItem>
|
||||||
<VCardTitle>
|
<VCardTitle>
|
||||||
<VIcon icon="mdi-order-alphabetical-ascending" size="small" class="me-2" />
|
<VIcon icon="mdi-order-alphabetical-ascending" size="small" class="me-2" />
|
||||||
设置标签顺序
|
{{ t('discover.setTabOrder') }}
|
||||||
</VCardTitle>
|
</VCardTitle>
|
||||||
<VDialogCloseBtn @click="orderConfigDialog = false" />
|
<VDialogCloseBtn @click="orderConfigDialog = false" />
|
||||||
</VCardItem>
|
</VCardItem>
|
||||||
<VDivider />
|
<VDivider />
|
||||||
<VCardText>
|
<VCardText>
|
||||||
<p class="settings-hint">拖动对标签页进行排序</p>
|
<p class="settings-hint">{{ t('discover.dragToReorder') }}</p>
|
||||||
<draggable
|
<draggable
|
||||||
v-model="discoverTabs"
|
v-model="discoverTabs"
|
||||||
handle=".cursor-move"
|
handle=".cursor-move"
|
||||||
@@ -210,7 +214,7 @@ onActivated(async () => {
|
|||||||
<template #prepend>
|
<template #prepend>
|
||||||
<VIcon icon="mdi-content-save" />
|
<VIcon icon="mdi-content-save" />
|
||||||
</template>
|
</template>
|
||||||
保存
|
{{ t('common.save') }}
|
||||||
</VBtn>
|
</VBtn>
|
||||||
</VCardText>
|
</VCardText>
|
||||||
</VCard>
|
</VCard>
|
||||||
|
|||||||
@@ -3,6 +3,10 @@ import api from '@/api'
|
|||||||
import { DownloaderConf } from '@/api/types'
|
import { DownloaderConf } from '@/api/types'
|
||||||
import DownloadingListView from '@/views/reorganize/DownloadingListView.vue'
|
import DownloadingListView from '@/views/reorganize/DownloadingListView.vue'
|
||||||
import NoDataFound from '@/components/NoDataFound.vue'
|
import NoDataFound from '@/components/NoDataFound.vue'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
|
// 国际化
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const activeTab = ref(route.query.tab)
|
const activeTab = ref(route.query.tab)
|
||||||
@@ -54,7 +58,7 @@ onActivated(async () => {
|
|||||||
<NoDataFound
|
<NoDataFound
|
||||||
v-else
|
v-else
|
||||||
error-code="404"
|
error-code="404"
|
||||||
error-title="没有下载器"
|
:error-title="t('downloading.noDownloader')"
|
||||||
error-description="请先在设置中正确配置并启用下载器。"
|
:error-description="t('downloading.configureDownloader')"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
+64
-58
@@ -2,94 +2,98 @@
|
|||||||
import api from '@/api'
|
import api from '@/api'
|
||||||
import { RecommendSource } from '@/api/types'
|
import { RecommendSource } from '@/api/types'
|
||||||
import MediaCardSlideView from '@/views/discover/MediaCardSlideView.vue'
|
import MediaCardSlideView from '@/views/discover/MediaCardSlideView.vue'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
|
// 国际化
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
// 当前选择的分类
|
// 当前选择的分类
|
||||||
const currentCategory = ref('全部')
|
const currentCategory = ref(t('recommend.all'))
|
||||||
|
|
||||||
const viewList = reactive<{ apipath: string; linkurl: string; title: string; type: string }[]>([
|
const viewList = reactive<{ apipath: string; linkurl: string; title: string; type: string }[]>([
|
||||||
{
|
{
|
||||||
apipath: 'recommend/tmdb_trending',
|
apipath: 'recommend/tmdb_trending',
|
||||||
linkurl: '/browse/recommend/tmdb_trending?title=流行趋势',
|
linkurl: '/browse/recommend/tmdb_trending?title=' + t('recommend.trendingNow'),
|
||||||
title: '流行趋势',
|
title: t('recommend.trendingNow'),
|
||||||
type: '榜单',
|
type: t('recommend.categoryRankings'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
apipath: 'recommend/douban_showing',
|
apipath: 'recommend/douban_showing',
|
||||||
linkurl: '/browse/recommend/douban_showing?title=正在热映',
|
linkurl: '/browse/recommend/douban_showing?title=' + t('recommend.nowShowing'),
|
||||||
title: '正在热映',
|
title: t('recommend.nowShowing'),
|
||||||
type: '电影',
|
type: t('recommend.categoryMovie'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
apipath: 'recommend/bangumi_calendar',
|
apipath: 'recommend/bangumi_calendar',
|
||||||
linkurl: '/browse/recommend/bangumi_calendar?title=Bangumi每日放送',
|
linkurl: '/browse/recommend/bangumi_calendar?title=' + t('recommend.bangumiDaily'),
|
||||||
title: 'Bangumi每日放送',
|
title: t('recommend.bangumiDaily'),
|
||||||
type: '动漫',
|
type: t('recommend.categoryAnime'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
apipath: 'recommend/tmdb_movies',
|
apipath: 'recommend/tmdb_movies',
|
||||||
linkurl: '/browse/recommend/tmdb_movies?title=TMDB热门电影',
|
linkurl: '/browse/recommend/tmdb_movies?title=' + t('recommend.tmdbHotMovies'),
|
||||||
title: 'TMDB热门电影',
|
title: t('recommend.tmdbHotMovies'),
|
||||||
type: '电影',
|
type: t('recommend.categoryMovie'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
apipath: 'recommend/tmdb_tvs?with_original_language=zh|en|ja|ko',
|
apipath: 'recommend/tmdb_tvs?with_original_language=zh|en|ja|ko',
|
||||||
linkurl: '/browse/recommend/tmdb_tvs??with_original_language=zh|en|ja|ko&title=TMDB热门电视剧',
|
linkurl: '/browse/recommend/tmdb_tvs??with_original_language=zh|en|ja|ko&title=' + t('recommend.tmdbHotTVShows'),
|
||||||
title: 'TMDB热门电视剧',
|
title: t('recommend.tmdbHotTVShows'),
|
||||||
type: '电视剧',
|
type: t('recommend.categoryTV'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
apipath: 'recommend/douban_movie_hot',
|
apipath: 'recommend/douban_movie_hot',
|
||||||
linkurl: '/browse/recommend/douban_movie_hot?title=豆瓣热门电影',
|
linkurl: '/browse/recommend/douban_movie_hot?title=' + t('recommend.doubanHotMovies'),
|
||||||
title: '豆瓣热门电影',
|
title: t('recommend.doubanHotMovies'),
|
||||||
type: '电影',
|
type: t('recommend.categoryMovie'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
apipath: 'recommend/douban_tv_hot',
|
apipath: 'recommend/douban_tv_hot',
|
||||||
linkurl: '/browse/recommend/douban_tv_hot?title=豆瓣热门电视剧',
|
linkurl: '/browse/recommend/douban_tv_hot?title=' + t('recommend.doubanHotTVShows'),
|
||||||
title: '豆瓣热门电视剧',
|
title: t('recommend.doubanHotTVShows'),
|
||||||
type: '电视剧',
|
type: t('recommend.categoryTV'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
apipath: 'recommend/douban_tv_animation',
|
apipath: 'recommend/douban_tv_animation',
|
||||||
linkurl: '/browse/recommend/douban_tv_animation?title=豆瓣热门动漫',
|
linkurl: '/browse/recommend/douban_tv_animation?title=' + t('recommend.doubanHotAnime'),
|
||||||
title: '豆瓣热门动漫',
|
title: t('recommend.doubanHotAnime'),
|
||||||
type: '动漫',
|
type: t('recommend.categoryAnime'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
apipath: 'recommend/douban_movies',
|
apipath: 'recommend/douban_movies',
|
||||||
linkurl: '/browse/recommend/douban_movies?title=豆瓣最新电影',
|
linkurl: '/browse/recommend/douban_movies?title=' + t('recommend.doubanNewMovies'),
|
||||||
title: '豆瓣最新电影',
|
title: t('recommend.doubanNewMovies'),
|
||||||
type: '电影',
|
type: t('recommend.categoryMovie'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
apipath: 'recommend/douban_tvs',
|
apipath: 'recommend/douban_tvs',
|
||||||
linkurl: '/browse/recommend/douban_tvs?title=豆瓣最新电视剧',
|
linkurl: '/browse/recommend/douban_tvs?title=' + t('recommend.doubanNewTVShows'),
|
||||||
title: '豆瓣最新电视剧',
|
title: t('recommend.doubanNewTVShows'),
|
||||||
type: '电视剧',
|
type: t('recommend.categoryTV'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
apipath: 'recommend/douban_movie_top250',
|
apipath: 'recommend/douban_movie_top250',
|
||||||
linkurl: '/browse/recommend/douban_movie_top250?title=电影TOP250',
|
linkurl: '/browse/recommend/douban_movie_top250?title=' + t('recommend.doubanTop250'),
|
||||||
title: '豆瓣电影TOP250',
|
title: t('recommend.doubanTop250'),
|
||||||
type: '榜单',
|
type: t('recommend.categoryRankings'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
apipath: 'recommend/douban_tv_weekly_chinese',
|
apipath: 'recommend/douban_tv_weekly_chinese',
|
||||||
linkurl: '/browse/recommend/douban_tv_weekly_chinese?title=豆瓣国产剧集榜',
|
linkurl: '/browse/recommend/douban_tv_weekly_chinese?title=' + t('recommend.doubanChineseTVRankings'),
|
||||||
title: '豆瓣国产剧集榜',
|
title: t('recommend.doubanChineseTVRankings'),
|
||||||
type: '榜单',
|
type: t('recommend.categoryRankings'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
apipath: 'recommend/douban_tv_weekly_global',
|
apipath: 'recommend/douban_tv_weekly_global',
|
||||||
linkurl: '/browse/recommend/douban_tv_weekly_global?title=豆瓣全球剧集榜',
|
linkurl: '/browse/recommend/douban_tv_weekly_global?title=' + t('recommend.doubanGlobalTVRankings'),
|
||||||
title: '豆瓣全球剧集榜',
|
title: t('recommend.doubanGlobalTVRankings'),
|
||||||
type: '榜单',
|
type: t('recommend.categoryRankings'),
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
// 计算当前分类下显示的视图
|
// 计算当前分类下显示的视图
|
||||||
const filteredViews = computed(() => {
|
const filteredViews = computed(() => {
|
||||||
if (currentCategory.value === '全部') {
|
if (currentCategory.value === t('recommend.all')) {
|
||||||
return viewList.filter(item => enableConfig.value[item.title])
|
return viewList.filter(item => enableConfig.value[item.title])
|
||||||
}
|
}
|
||||||
return viewList.filter(item => enableConfig.value[item.title] && item.type === currentCategory.value)
|
return viewList.filter(item => enableConfig.value[item.title] && item.type === currentCategory.value)
|
||||||
@@ -158,29 +162,29 @@ async function saveConfig() {
|
|||||||
// 标签图标映射
|
// 标签图标映射
|
||||||
const categoryItems: Record<string, string>[] = [
|
const categoryItems: Record<string, string>[] = [
|
||||||
{
|
{
|
||||||
title: '全部',
|
title: t('recommend.all'),
|
||||||
icon: 'mdi-filmstrip-box-multiple',
|
icon: 'mdi-filmstrip-box-multiple',
|
||||||
tab: '全部',
|
tab: t('recommend.all'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '电影',
|
title: t('recommend.categoryMovie'),
|
||||||
icon: 'mdi-movie',
|
icon: 'mdi-movie',
|
||||||
tab: '电影',
|
tab: t('recommend.categoryMovie'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '电视剧',
|
title: t('recommend.categoryTV'),
|
||||||
icon: 'mdi-television-classic',
|
icon: 'mdi-television-classic',
|
||||||
tab: '电视剧',
|
tab: t('recommend.categoryTV'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '动漫',
|
title: t('recommend.categoryAnime'),
|
||||||
icon: 'mdi-animation',
|
icon: 'mdi-animation',
|
||||||
tab: '动漫',
|
tab: t('recommend.categoryAnime'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '榜单',
|
title: t('recommend.categoryRankings'),
|
||||||
icon: 'mdi-trophy',
|
icon: 'mdi-trophy',
|
||||||
tab: '榜单',
|
tab: t('recommend.categoryRankings'),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -221,8 +225,10 @@ onActivated(async () => {
|
|||||||
|
|
||||||
<div v-if="filteredViews.length === 0" class="empty-category">
|
<div v-if="filteredViews.length === 0" class="empty-category">
|
||||||
<VIcon icon="mdi-alert-circle-outline" size="large" class="empty-icon" />
|
<VIcon icon="mdi-alert-circle-outline" size="large" class="empty-icon" />
|
||||||
<p class="empty-text">当前分类下没有可显示的内容</p>
|
<p class="empty-text">{{ t('recommend.noCategoryContent') }}</p>
|
||||||
<VBtn color="primary" variant="tonal" size="small" @click="dialog = true"> 设置显示内容 </VBtn>
|
<VBtn color="primary" variant="tonal" size="small" @click="dialog = true">
|
||||||
|
{{ t('recommend.configureContent') }}
|
||||||
|
</VBtn>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -232,13 +238,13 @@ onActivated(async () => {
|
|||||||
<VCardItem class="settings-card-header">
|
<VCardItem class="settings-card-header">
|
||||||
<VCardTitle>
|
<VCardTitle>
|
||||||
<VIcon icon="mdi-tune" size="small" class="me-2" />
|
<VIcon icon="mdi-tune" size="small" class="me-2" />
|
||||||
自定义内容
|
{{ t('recommend.customizeContent') }}
|
||||||
</VCardTitle>
|
</VCardTitle>
|
||||||
<VDialogCloseBtn @click="dialog = false" />
|
<VDialogCloseBtn @click="dialog = false" />
|
||||||
</VCardItem>
|
</VCardItem>
|
||||||
<VDivider />
|
<VDivider />
|
||||||
<VCardText>
|
<VCardText>
|
||||||
<p class="settings-hint">选择您想在页面显示的内容</p>
|
<p class="settings-hint">{{ t('recommend.selectContentToDisplay') }}</p>
|
||||||
<div class="settings-grid">
|
<div class="settings-grid">
|
||||||
<div
|
<div
|
||||||
v-for="(item, index) in viewList"
|
v-for="(item, index) in viewList"
|
||||||
@@ -266,17 +272,17 @@ onActivated(async () => {
|
|||||||
<VDivider />
|
<VDivider />
|
||||||
<VCardActions class="pt-5">
|
<VCardActions class="pt-5">
|
||||||
<VBtn variant="text" @click="Object.keys(enableConfig).forEach(key => (enableConfig[key] = true))">
|
<VBtn variant="text" @click="Object.keys(enableConfig).forEach(key => (enableConfig[key] = true))">
|
||||||
全选
|
{{ t('recommend.selectAll') }}
|
||||||
</VBtn>
|
</VBtn>
|
||||||
<VBtn variant="text" @click="Object.keys(enableConfig).forEach(key => (enableConfig[key] = false))">
|
<VBtn variant="text" @click="Object.keys(enableConfig).forEach(key => (enableConfig[key] = false))">
|
||||||
全不选
|
{{ t('recommend.selectNone') }}
|
||||||
</VBtn>
|
</VBtn>
|
||||||
<VSpacer />
|
<VSpacer />
|
||||||
<VBtn @click="saveConfig" variant="elevated" color="primary" class="px-5">
|
<VBtn @click="saveConfig" variant="elevated" color="primary" class="px-5">
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
<VIcon icon="mdi-content-save" />
|
<VIcon icon="mdi-content-save" />
|
||||||
</template>
|
</template>
|
||||||
保存
|
{{ t('common.save') }}
|
||||||
</VBtn>
|
</VBtn>
|
||||||
</VCardActions>
|
</VCardActions>
|
||||||
</VCard>
|
</VCard>
|
||||||
|
|||||||
+21
-11
@@ -4,6 +4,10 @@ import api from '@/api'
|
|||||||
import type { Context } from '@/api/types'
|
import type { Context } from '@/api/types'
|
||||||
import TorrentCardListView from '@/views/torrent/TorrentCardListView.vue'
|
import TorrentCardListView from '@/views/torrent/TorrentCardListView.vue'
|
||||||
import TorrentRowListView from '@/views/torrent/TorrentRowListView.vue'
|
import TorrentRowListView from '@/views/torrent/TorrentRowListView.vue'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
|
// 国际化
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
// 路由参数
|
// 路由参数
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@@ -51,14 +55,14 @@ const progressValue = ref(0)
|
|||||||
const progressEventSource = ref<EventSource>()
|
const progressEventSource = ref<EventSource>()
|
||||||
|
|
||||||
// 错误标题
|
// 错误标题
|
||||||
const errorTitle = ref('没有数据')
|
const errorTitle = ref(t('resource.noData'))
|
||||||
|
|
||||||
// 错误描述
|
// 错误描述
|
||||||
const errorDescription = ref('未搜索到任何资源')
|
const errorDescription = ref(t('resource.noResourceFound'))
|
||||||
|
|
||||||
// 使用SSE监听加载进度
|
// 使用SSE监听加载进度
|
||||||
function startLoadingProgress() {
|
function startLoadingProgress() {
|
||||||
progressText.value = '正在搜索,请稍候...'
|
progressText.value = t('resource.searching')
|
||||||
progressValue.value = 10 // 初始进度设为10%,确保进度条显示
|
progressValue.value = 10 // 初始进度设为10%,确保进度条显示
|
||||||
progressEventSource.value = new EventSource(`${import.meta.env.VITE_API_BASE_URL}system/progress/search`)
|
progressEventSource.value = new EventSource(`${import.meta.env.VITE_API_BASE_URL}system/progress/search`)
|
||||||
progressEventSource.value.onmessage = event => {
|
progressEventSource.value.onmessage = event => {
|
||||||
@@ -199,14 +203,20 @@ onUnmounted(() => {
|
|||||||
<!-- 精简标题栏 -->
|
<!-- 精简标题栏 -->
|
||||||
<VCard v-if="isRefreshed" class="search-header d-flex align-center mb-3">
|
<VCard v-if="isRefreshed" class="search-header d-flex align-center mb-3">
|
||||||
<div class="search-info-container d-flex align-center flex-wrap">
|
<div class="search-info-container d-flex align-center flex-wrap">
|
||||||
<div class="search-title text-primary">资源搜索结果</div>
|
<div class="search-title text-primary">{{ t('resource.searchResults') }}</div>
|
||||||
<div class="search-tags d-flex flex-wrap">
|
<div class="search-tags d-flex flex-wrap">
|
||||||
<VChip v-if="keyword" class="search-tag" color="primary" size="small" variant="flat">
|
<VChip v-if="keyword" class="search-tag" color="primary" size="small" variant="flat">
|
||||||
关键词: {{ keyword }}
|
{{ t('resource.keyword') }}: {{ keyword }}
|
||||||
|
</VChip>
|
||||||
|
<VChip v-if="title" class="search-tag" color="primary" size="small" variant="flat">
|
||||||
|
{{ t('resource.title') }}: {{ title }}
|
||||||
|
</VChip>
|
||||||
|
<VChip v-if="year" class="search-tag" color="primary" size="small" variant="flat">
|
||||||
|
{{ t('resource.year') }}: {{ year }}
|
||||||
|
</VChip>
|
||||||
|
<VChip v-if="season" class="search-tag" color="primary" size="small" variant="flat">
|
||||||
|
{{ t('resource.season') }}: {{ season }}
|
||||||
</VChip>
|
</VChip>
|
||||||
<VChip v-if="title" class="search-tag" color="primary" size="small" variant="flat"> 标题: {{ title }} </VChip>
|
|
||||||
<VChip v-if="year" class="search-tag" color="primary" size="small" variant="flat"> 年份: {{ year }} </VChip>
|
|
||||||
<VChip v-if="season" class="search-tag" color="primary" size="small" variant="flat"> 季: {{ season }} </VChip>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<VSpacer />
|
<VSpacer />
|
||||||
@@ -232,7 +242,7 @@ onUnmounted(() => {
|
|||||||
<div class="pulse-circle"></div>
|
<div class="pulse-circle"></div>
|
||||||
<div class="pulse-circle"></div>
|
<div class="pulse-circle"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="view-changing-text">切换视图</div>
|
<div class="view-changing-text">{{ t('resource.switchingView') }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</VFadeTransition>
|
</VFadeTransition>
|
||||||
@@ -257,7 +267,7 @@ onUnmounted(() => {
|
|||||||
<!-- 无数据显示 -->
|
<!-- 无数据显示 -->
|
||||||
<div v-else-if="isRefreshed && !isViewChanging" class="d-flex flex-column align-center justify-center py-8">
|
<div v-else-if="isRefreshed && !isViewChanging" class="d-flex flex-column align-center justify-center py-8">
|
||||||
<NoDataFound :errorTitle="errorTitle" :errorDescription="errorDescription" />
|
<NoDataFound :errorTitle="errorTitle" :errorDescription="errorDescription" />
|
||||||
<VBtn class="mt-4" color="primary" prepend-icon="mdi-magnify" to="/"> 返回首页 </VBtn>
|
<VBtn class="mt-4" color="primary" prepend-icon="mdi-magnify" to="/">{{ t('resource.backToHome') }}</VBtn>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 初始加载状态 -->
|
<!-- 初始加载状态 -->
|
||||||
@@ -269,7 +279,7 @@ onUnmounted(() => {
|
|||||||
<div class="wave-dot"></div>
|
<div class="wave-dot"></div>
|
||||||
<div class="wave-dot"></div>
|
<div class="wave-dot"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="initial-loading-text">搜索中</div>
|
<div class="initial-loading-text">{{ t('resource.searching') }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 滚动到顶部按钮 -->
|
<!-- 滚动到顶部按钮 -->
|
||||||
|
|||||||
@@ -3,9 +3,13 @@ import SubscribeListView from '@/views/subscribe/SubscribeListView.vue'
|
|||||||
import SubscribePopularView from '@/views/subscribe/SubscribePopularView.vue'
|
import SubscribePopularView from '@/views/subscribe/SubscribePopularView.vue'
|
||||||
import SubscribeShareView from '@/views/subscribe/SubscribeShareView.vue'
|
import SubscribeShareView from '@/views/subscribe/SubscribeShareView.vue'
|
||||||
import SubscribeEditDialog from '@/components/dialog/SubscribeEditDialog.vue'
|
import SubscribeEditDialog from '@/components/dialog/SubscribeEditDialog.vue'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
import { getSubscribeMovieTabs, getSubscribeTvTabs } from '@/router/i18n-menu'
|
import { getSubscribeMovieTabs, getSubscribeTvTabs } from '@/router/i18n-menu'
|
||||||
|
|
||||||
|
// 国际化
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
|
||||||
const subType = route.meta.subType?.toString()
|
const subType = route.meta.subType?.toString()
|
||||||
@@ -69,12 +73,12 @@ const searchShares = () => {
|
|||||||
<VCardItem>
|
<VCardItem>
|
||||||
<VCardTitle>
|
<VCardTitle>
|
||||||
<VIcon icon="mdi-filter-multiple-outline" class="mr-2" />
|
<VIcon icon="mdi-filter-multiple-outline" class="mr-2" />
|
||||||
筛选订阅
|
{{ t('subscribe.filterSubscriptions') }}
|
||||||
</VCardTitle>
|
</VCardTitle>
|
||||||
<VDialogCloseBtn @click="filterSubscribeDialog = false" />
|
<VDialogCloseBtn @click="filterSubscribeDialog = false" />
|
||||||
</VCardItem>
|
</VCardItem>
|
||||||
<VCardText>
|
<VCardText>
|
||||||
<VTextField v-model="subscribeFilter" label="名称" clearable density="comfortable" />
|
<VTextField v-model="subscribeFilter" :label="t('subscribe.name')" clearable density="comfortable" />
|
||||||
</VCardText>
|
</VCardText>
|
||||||
</VCard>
|
</VCard>
|
||||||
</VMenu>
|
</VMenu>
|
||||||
@@ -99,14 +103,14 @@ const searchShares = () => {
|
|||||||
<VCardItem>
|
<VCardItem>
|
||||||
<VCardTitle>
|
<VCardTitle>
|
||||||
<VIcon icon="mdi-movie-search-outline" class="mr-2" />
|
<VIcon icon="mdi-movie-search-outline" class="mr-2" />
|
||||||
搜索订阅分享
|
{{ t('subscribe.searchShares') }}
|
||||||
</VCardTitle>
|
</VCardTitle>
|
||||||
<VDialogCloseBtn @click="searchShareDialog = false" />
|
<VDialogCloseBtn @click="searchShareDialog = false" />
|
||||||
</VCardItem>
|
</VCardItem>
|
||||||
<VCardText>
|
<VCardText>
|
||||||
<VTextField v-model="shareKeyword" label="关键词" clearable density="comfortable">
|
<VTextField v-model="shareKeyword" :label="t('subscribe.keyword')" clearable density="comfortable">
|
||||||
<template #append>
|
<template #append>
|
||||||
<VBtn prepend-icon="mdi-magnify" color="primary" @click="searchShares">搜索</VBtn>
|
<VBtn prepend-icon="mdi-magnify" color="primary" @click="searchShares">{{ t('common.search') }}</VBtn>
|
||||||
</template>
|
</template>
|
||||||
</VTextField>
|
</VTextField>
|
||||||
</VCardText>
|
</VCardText>
|
||||||
|
|||||||
@@ -2,6 +2,10 @@
|
|||||||
import { cloneDeepWith } from 'lodash-es'
|
import { cloneDeepWith } from 'lodash-es'
|
||||||
import type { Context } from '@/api/types'
|
import type { Context } from '@/api/types'
|
||||||
import TorrentCard from '@/components/cards/TorrentCard.vue'
|
import TorrentCard from '@/components/cards/TorrentCard.vue'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
|
// 国际化
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
interface SearchTorrent extends Context {
|
interface SearchTorrent extends Context {
|
||||||
more?: Array<Context>
|
more?: Array<Context>
|
||||||
@@ -34,21 +38,21 @@ const filterForm: Record<string, string[]> = reactive({
|
|||||||
// 排序选项
|
// 排序选项
|
||||||
const sortField = ref('default')
|
const sortField = ref('default')
|
||||||
const sortTitles: Record<string, string> = {
|
const sortTitles: Record<string, string> = {
|
||||||
default: '默认',
|
default: t('torrent.sortDefault'),
|
||||||
site: '站点',
|
site: t('torrent.sortSite'),
|
||||||
size: '大小',
|
size: t('torrent.sortSize'),
|
||||||
seeder: '做种数',
|
seeder: t('torrent.sortSeeder'),
|
||||||
}
|
}
|
||||||
|
|
||||||
// 过滤项映射(保持中文标题)
|
// 过滤项映射
|
||||||
const filterTitles: Record<string, string> = {
|
const filterTitles: Record<string, string> = {
|
||||||
site: '站点',
|
site: t('torrent.filterSite'),
|
||||||
season: '季集',
|
season: t('torrent.filterSeason'),
|
||||||
freeState: '促销状态',
|
freeState: t('torrent.filterFreeState'),
|
||||||
videoCode: '视频编码',
|
videoCode: t('torrent.filterVideoCode'),
|
||||||
edition: '质量',
|
edition: t('torrent.filterEdition'),
|
||||||
resolution: '分辨率',
|
resolution: t('torrent.filterResolution'),
|
||||||
releaseGroup: '制作组',
|
releaseGroup: t('torrent.filterReleaseGroup'),
|
||||||
}
|
}
|
||||||
|
|
||||||
// 统一存储过滤选项
|
// 统一存储过滤选项
|
||||||
|
|||||||
Reference in New Issue
Block a user