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:
@@ -5,6 +5,10 @@ import { doneNProgress, startNProgress } from '@/api/nprogress'
|
|||||||
import type { DownloaderConf, MediaInfo, TorrentInfo, TransferDirectoryConf } from '@/api/types'
|
import type { DownloaderConf, MediaInfo, TorrentInfo, TransferDirectoryConf } from '@/api/types'
|
||||||
import { formatFileSize } from '@/@core/utils/formatters'
|
import { formatFileSize } from '@/@core/utils/formatters'
|
||||||
import { VCardTitle, VChip } from 'vuetify/lib/components/index.mjs'
|
import { VCardTitle, VChip } from 'vuetify/lib/components/index.mjs'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
|
// 多语言支持
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
// 输入参数
|
// 输入参数
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -38,7 +42,9 @@ const loading = ref(false)
|
|||||||
const icon = computed(() => (loading.value ? 'mdi-progress-download' : 'mdi-download'))
|
const icon = computed(() => (loading.value ? 'mdi-progress-download' : 'mdi-download'))
|
||||||
|
|
||||||
// 计算按钮文字
|
// 计算按钮文字
|
||||||
const buttonText = computed(() => (loading.value ? '下载中...' : '开始下载'))
|
const buttonText = computed(() =>
|
||||||
|
loading.value ? t('dialog.addDownload.downloading') : t('dialog.addDownload.startDownload'),
|
||||||
|
)
|
||||||
|
|
||||||
// 加载目录设置
|
// 加载目录设置
|
||||||
async function loadDirectories() {
|
async function loadDirectories() {
|
||||||
@@ -96,12 +102,20 @@ async function addDownload() {
|
|||||||
|
|
||||||
if (result && result.success) {
|
if (result && result.success) {
|
||||||
// 添加下载成功
|
// 添加下载成功
|
||||||
$toast.success(`${props.torrent?.site_name} ${props.torrent?.title} 下载成功!`)
|
$toast.success(
|
||||||
|
t('dialog.addDownload.downloadSuccess', { site: props.torrent?.site_name, title: props.torrent?.title }),
|
||||||
|
)
|
||||||
// 下载成功,返回链接
|
// 下载成功,返回链接
|
||||||
emit('done', props.torrent?.enclosure)
|
emit('done', props.torrent?.enclosure)
|
||||||
} else {
|
} else {
|
||||||
// 添加下载失败
|
// 添加下载失败
|
||||||
$toast.error(`${props.torrent?.site_name} ${props.torrent?.title} 下载失败:${result?.message}!`)
|
$toast.error(
|
||||||
|
t('dialog.addDownload.downloadFailed', {
|
||||||
|
site: props.torrent?.site_name,
|
||||||
|
title: props.torrent?.title,
|
||||||
|
message: result?.message,
|
||||||
|
}),
|
||||||
|
)
|
||||||
// 下载失败,返回错误原因
|
// 下载失败,返回错误原因
|
||||||
emit('error', result?.message)
|
emit('error', result?.message)
|
||||||
}
|
}
|
||||||
@@ -123,7 +137,7 @@ onMounted(() => {
|
|||||||
<VCardTitle class="py-4 me-12">
|
<VCardTitle class="py-4 me-12">
|
||||||
<VIcon icon="mdi-download" class="me-2" />
|
<VIcon icon="mdi-download" class="me-2" />
|
||||||
<span v-if="title">{{ torrent?.site_name }} - {{ title }}</span>
|
<span v-if="title">{{ torrent?.site_name }} - {{ title }}</span>
|
||||||
<span v-else>确认下载</span>
|
<span v-else>{{ t('dialog.addDownload.confirmDownload') }}</span>
|
||||||
</VCardTitle>
|
</VCardTitle>
|
||||||
<VDialogCloseBtn @click="emit('close')" />
|
<VDialogCloseBtn @click="emit('close')" />
|
||||||
<VDivider />
|
<VDivider />
|
||||||
@@ -165,9 +179,9 @@ onMounted(() => {
|
|||||||
v-model="selectedDownloader"
|
v-model="selectedDownloader"
|
||||||
:items="downloaderOptions"
|
:items="downloaderOptions"
|
||||||
size="small"
|
size="small"
|
||||||
label="下载器(默认)"
|
:label="t('dialog.addDownload.downloader')"
|
||||||
variant="underlined"
|
variant="underlined"
|
||||||
placeholder="留空默认"
|
:placeholder="t('dialog.addDownload.defaultPlaceholder')"
|
||||||
density="compact"
|
density="compact"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
@@ -175,9 +189,9 @@ onMounted(() => {
|
|||||||
<VCombobox
|
<VCombobox
|
||||||
v-model="selectedDirectory"
|
v-model="selectedDirectory"
|
||||||
:items="targetDirectories"
|
:items="targetDirectories"
|
||||||
label="保存目录(自动)"
|
:label="t('dialog.addDownload.saveDirectory')"
|
||||||
size="small"
|
size="small"
|
||||||
placeholder="留空自动匹配"
|
:placeholder="t('dialog.addDownload.autoPlaceholder')"
|
||||||
variant="underlined"
|
variant="underlined"
|
||||||
density="compact"
|
density="compact"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
|
// 多语言支持
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
// 输入参数
|
// 输入参数
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
title: String,
|
title: String,
|
||||||
@@ -27,7 +32,9 @@ function handleImport() {
|
|||||||
</VCardText>
|
</VCardText>
|
||||||
<VCardActions>
|
<VCardActions>
|
||||||
<VSpacer />
|
<VSpacer />
|
||||||
<VBtn variant="elevated" @click="handleImport" prepend-icon="mdi-import" class="px-5 me-3"> 导入 </VBtn>
|
<VBtn variant="elevated" @click="handleImport" prepend-icon="mdi-import" class="px-5 me-3">
|
||||||
|
{{ t('dialog.importCode.import') }}
|
||||||
|
</VBtn>
|
||||||
</VCardActions>
|
</VCardActions>
|
||||||
</VCard>
|
</VCard>
|
||||||
</VDialog>
|
</VDialog>
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Context } from '@/api/types'
|
import { Context } from '@/api/types'
|
||||||
import MediaInfoCard from '../cards/MediaInfoCard.vue'
|
import MediaInfoCard from '../cards/MediaInfoCard.vue'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
|
// 多语言支持
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
// 输入参数
|
// 输入参数
|
||||||
defineProps({
|
defineProps({
|
||||||
|
|||||||
@@ -1,15 +1,19 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
value: Number,
|
value: Number,
|
||||||
text: String,
|
text: String,
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<!-- 手动整理进度框 -->
|
<!-- Progress Dialog -->
|
||||||
<VDialog :scrim="false" width="25rem">
|
<VDialog :scrim="false" width="25rem">
|
||||||
<VCard elevation="3" color="primary">
|
<VCard elevation="3" color="primary">
|
||||||
<VCardText class="text-center">
|
<VCardText class="text-center">
|
||||||
{{ props.text }}
|
{{ props.text || t('dialog.progress.processing') }}
|
||||||
<VProgressLinear color="white" class="mb-0 mt-1" :model-value="props.value" indeterminate />
|
<VProgressLinear color="white" class="mb-0 mt-1" :model-value="props.value" indeterminate />
|
||||||
</VCardText>
|
</VCardText>
|
||||||
</VCard>
|
</VCard>
|
||||||
|
|||||||
@@ -5,6 +5,10 @@ import { getNavMenus, getSettingTabs } from '@/router/i18n-menu'
|
|||||||
import { NavMenu } from '@/@layouts/types'
|
import { NavMenu } from '@/@layouts/types'
|
||||||
import { useUserStore } from '@/stores'
|
import { useUserStore } from '@/stores'
|
||||||
import SearchSiteDialog from '@/components/dialog/SearchSiteDialog.vue'
|
import SearchSiteDialog from '@/components/dialog/SearchSiteDialog.vue'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
|
// 多语言支持
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
// 定义props,接收modelValue
|
// 定义props,接收modelValue
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
@@ -89,7 +93,7 @@ function getMenus(): NavMenu[] {
|
|||||||
item =>
|
item =>
|
||||||
item &&
|
item &&
|
||||||
menus.push({
|
menus.push({
|
||||||
title: '设定 -> ' + item.title,
|
title: t('setting') + ' -> ' + item.title,
|
||||||
icon: item.icon,
|
icon: item.icon,
|
||||||
to: `/setting?tab=${item.tab}`,
|
to: `/setting?tab=${item.tab}`,
|
||||||
header: '',
|
header: '',
|
||||||
@@ -311,7 +315,7 @@ onMounted(() => {
|
|||||||
density="comfortable"
|
density="comfortable"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
class="search-input"
|
class="search-input"
|
||||||
placeholder="输入关键词搜索..."
|
:placeholder="t('dialog.searchBar.searchPlaceholder')"
|
||||||
@keydown.enter="searchMedia('media')"
|
@keydown.enter="searchMedia('media')"
|
||||||
hide-details
|
hide-details
|
||||||
clearable
|
clearable
|
||||||
@@ -330,7 +334,9 @@ onMounted(() => {
|
|||||||
<!-- 有搜索词时显示结果 -->
|
<!-- 有搜索词时显示结果 -->
|
||||||
<VList lines="two" v-if="searchWord" class="search-list py-2">
|
<VList lines="two" v-if="searchWord" class="search-list py-2">
|
||||||
<!-- 搜索结果分组标题 -->
|
<!-- 搜索结果分组标题 -->
|
||||||
<VListSubheader class="font-weight-medium text-uppercase py-2 px-4 px-sm-6"> 媒体 </VListSubheader>
|
<VListSubheader class="font-weight-medium text-uppercase py-2 px-4 px-sm-6">
|
||||||
|
{{ t('media.movie') }}
|
||||||
|
</VListSubheader>
|
||||||
|
|
||||||
<!-- 媒体搜索选项 -->
|
<!-- 媒体搜索选项 -->
|
||||||
<VHover>
|
<VHover>
|
||||||
@@ -352,9 +358,12 @@ onMounted(() => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<VListItemTitle class="font-weight-medium"> 电影、电视剧 </VListItemTitle>
|
<VListItemTitle class="font-weight-medium"
|
||||||
|
>{{ t('recommend.categoryMovie') }}、{{ t('recommend.categoryTV') }}</VListItemTitle
|
||||||
|
>
|
||||||
<VListItemSubtitle class="text-body-2 text-medium-emphasis mt-1">
|
<VListItemSubtitle class="text-body-2 text-medium-emphasis mt-1">
|
||||||
搜索 <span class="primary-text font-weight-medium">{{ searchWord }}</span> 相关的影视作品
|
{{ t('common.search') }} <span class="primary-text font-weight-medium">{{ searchWord }}</span>
|
||||||
|
{{ t('resource.title') }}
|
||||||
</VListItemSubtitle>
|
</VListItemSubtitle>
|
||||||
<template #append>
|
<template #append>
|
||||||
<VIcon v-if="hover.isHovering" icon="mdi-chevron-right" color="primary" />
|
<VIcon v-if="hover.isHovering" icon="mdi-chevron-right" color="primary" />
|
||||||
@@ -382,9 +391,10 @@ onMounted(() => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<VListItemTitle class="font-weight-medium"> 系列合集 </VListItemTitle>
|
<VListItemTitle class="font-weight-medium">{{ t('dialog.searchBar.collections') }}</VListItemTitle>
|
||||||
<VListItemSubtitle class="text-body-2 text-medium-emphasis mt-1">
|
<VListItemSubtitle class="text-body-2 text-medium-emphasis mt-1">
|
||||||
搜索 <span class="primary-text font-weight-medium">{{ searchWord }}</span> 相关的系列作品
|
{{ t('common.search') }} <span class="primary-text font-weight-medium">{{ searchWord }}</span>
|
||||||
|
{{ t('dialog.searchBar.collectionSearch') }}
|
||||||
</VListItemSubtitle>
|
</VListItemSubtitle>
|
||||||
<template #append>
|
<template #append>
|
||||||
<VIcon v-if="hover.isHovering" icon="mdi-chevron-right" color="primary" />
|
<VIcon v-if="hover.isHovering" icon="mdi-chevron-right" color="primary" />
|
||||||
@@ -412,9 +422,10 @@ onMounted(() => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<VListItemTitle class="font-weight-medium"> 演职人员 </VListItemTitle>
|
<VListItemTitle class="font-weight-medium">{{ t('browse.actor') }}</VListItemTitle>
|
||||||
<VListItemSubtitle class="text-body-2 text-medium-emphasis mt-1">
|
<VListItemSubtitle class="text-body-2 text-medium-emphasis mt-1">
|
||||||
搜索 <span class="primary-text font-weight-medium">{{ searchWord }}</span> 相关的演员、导演等
|
{{ t('common.search') }} <span class="primary-text font-weight-medium">{{ searchWord }}</span>
|
||||||
|
{{ t('dialog.searchBar.actorSearch') }}
|
||||||
</VListItemSubtitle>
|
</VListItemSubtitle>
|
||||||
<template #append>
|
<template #append>
|
||||||
<VIcon v-if="hover.isHovering" icon="mdi-chevron-right" color="primary" />
|
<VIcon v-if="hover.isHovering" icon="mdi-chevron-right" color="primary" />
|
||||||
@@ -438,9 +449,10 @@ onMounted(() => {
|
|||||||
<VIcon icon="mdi-history" :color="hover.isHovering ? 'primary' : 'medium-emphasis'" size="small" />
|
<VIcon icon="mdi-history" :color="hover.isHovering ? 'primary' : 'medium-emphasis'" size="small" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<VListItemTitle class="font-weight-medium"> 整理记录 </VListItemTitle>
|
<VListItemTitle class="font-weight-medium">{{ t('navItems.history') }}</VListItemTitle>
|
||||||
<VListItemSubtitle class="text-body-2 text-medium-emphasis mt-1">
|
<VListItemSubtitle class="text-body-2 text-medium-emphasis mt-1">
|
||||||
搜索 <span class="primary-text font-weight-medium">{{ searchWord }}</span> 相关的历史记录
|
{{ t('common.search') }} <span class="primary-text font-weight-medium">{{ searchWord }}</span>
|
||||||
|
{{ t('dialog.searchBar.historySearch') }}
|
||||||
</VListItemSubtitle>
|
</VListItemSubtitle>
|
||||||
<template #append>
|
<template #append>
|
||||||
<VIcon v-if="hover.isHovering" icon="mdi-chevron-right" color="primary" />
|
<VIcon v-if="hover.isHovering" icon="mdi-chevron-right" color="primary" />
|
||||||
@@ -452,7 +464,9 @@ onMounted(() => {
|
|||||||
<!-- 其他搜索结果 -->
|
<!-- 其他搜索结果 -->
|
||||||
<template v-if="matchedSubscribeItems.length > 0">
|
<template v-if="matchedSubscribeItems.length > 0">
|
||||||
<VDivider class="mx-4 mx-sm-6 my-2 search-divider" />
|
<VDivider class="mx-4 mx-sm-6 my-2 search-divider" />
|
||||||
<VListSubheader class="font-weight-medium text-uppercase py-2 px-4 px-sm-6"> 订阅 </VListSubheader>
|
<VListSubheader class="font-weight-medium text-uppercase py-2 px-4 px-sm-6">{{
|
||||||
|
t('dialog.searchBar.subscriptions')
|
||||||
|
}}</VListSubheader>
|
||||||
|
|
||||||
<VHover v-for="subscribe in matchedSubscribeItems" :key="subscribe.id">
|
<VHover v-for="subscribe in matchedSubscribeItems" :key="subscribe.id">
|
||||||
<template #default="hover">
|
<template #default="hover">
|
||||||
@@ -475,7 +489,9 @@ onMounted(() => {
|
|||||||
</template>
|
</template>
|
||||||
<VListItemTitle class="font-weight-medium">
|
<VListItemTitle class="font-weight-medium">
|
||||||
{{ subscribe.name
|
{{ subscribe.name
|
||||||
}}<span v-if="subscribe.season" class="text-body-2"> 第 {{ subscribe.season }} 季</span>
|
}}<span v-if="subscribe.season" class="text-body-2">
|
||||||
|
{{ t('resource.season') }} {{ subscribe.season }}</span
|
||||||
|
>
|
||||||
</VListItemTitle>
|
</VListItemTitle>
|
||||||
<VListItemSubtitle class="text-body-2 text-medium-emphasis mt-1">
|
<VListItemSubtitle class="text-body-2 text-medium-emphasis mt-1">
|
||||||
{{ subscribe.type }}
|
{{ subscribe.type }}
|
||||||
@@ -490,7 +506,9 @@ onMounted(() => {
|
|||||||
|
|
||||||
<template v-if="matchedMenuItems.length > 0">
|
<template v-if="matchedMenuItems.length > 0">
|
||||||
<VDivider class="mx-4 mx-sm-6 my-2 search-divider" />
|
<VDivider class="mx-4 mx-sm-6 my-2 search-divider" />
|
||||||
<VListSubheader class="font-weight-medium text-uppercase py-2 px-4 px-sm-6"> 功能 </VListSubheader>
|
<VListSubheader class="font-weight-medium text-uppercase py-2 px-4 px-sm-6">{{
|
||||||
|
t('dialog.searchBar.functions')
|
||||||
|
}}</VListSubheader>
|
||||||
|
|
||||||
<VHover v-for="menu in matchedMenuItems" :key="menu.title">
|
<VHover v-for="menu in matchedMenuItems" :key="menu.title">
|
||||||
<template #default="hover">
|
<template #default="hover">
|
||||||
@@ -527,7 +545,9 @@ onMounted(() => {
|
|||||||
|
|
||||||
<template v-if="matchedPluginItems.length > 0">
|
<template v-if="matchedPluginItems.length > 0">
|
||||||
<VDivider class="mx-4 mx-sm-6 my-2 search-divider" />
|
<VDivider class="mx-4 mx-sm-6 my-2 search-divider" />
|
||||||
<VListSubheader class="font-weight-medium text-uppercase py-2 px-4 px-sm-6"> 插件 </VListSubheader>
|
<VListSubheader class="font-weight-medium text-uppercase py-2 px-4 px-sm-6">{{
|
||||||
|
t('dialog.searchBar.plugins')
|
||||||
|
}}</VListSubheader>
|
||||||
|
|
||||||
<VHover v-for="plugin in matchedPluginItems" :key="plugin.id">
|
<VHover v-for="plugin in matchedPluginItems" :key="plugin.id">
|
||||||
<template #default="hover">
|
<template #default="hover">
|
||||||
@@ -561,7 +581,9 @@ onMounted(() => {
|
|||||||
<!-- 将站点资源搜索移到最底部 -->
|
<!-- 将站点资源搜索移到最底部 -->
|
||||||
<template v-if="searchWord">
|
<template v-if="searchWord">
|
||||||
<VDivider class="mx-4 mx-sm-6 my-2 search-divider" />
|
<VDivider class="mx-4 mx-sm-6 my-2 search-divider" />
|
||||||
<VListSubheader class="font-weight-medium text-uppercase py-2 px-4 px-sm-6"> 站点资源 </VListSubheader>
|
<VListSubheader class="font-weight-medium text-uppercase py-2 px-4 px-sm-6">{{
|
||||||
|
t('dialog.searchBar.siteResources')
|
||||||
|
}}</VListSubheader>
|
||||||
|
|
||||||
<VCard class="mx-3 mx-sm-6 mb-4 mt-2 site-search-card">
|
<VCard class="mx-3 mx-sm-6 mb-4 mt-2 site-search-card">
|
||||||
<VCardText class="pa-3 pa-sm-4">
|
<VCardText class="pa-3 pa-sm-4">
|
||||||
@@ -571,9 +593,10 @@ onMounted(() => {
|
|||||||
<VIcon icon="mdi-file-search" color="primary" size="small" />
|
<VIcon icon="mdi-file-search" color="primary" size="small" />
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-grow-1">
|
<div class="flex-grow-1">
|
||||||
<div class="font-weight-medium text-body-1">在站点中搜索种子资源</div>
|
<div class="font-weight-medium text-body-1">{{ t('dialog.searchBar.searchInSites') }}</div>
|
||||||
<div class="text-caption text-medium-emphasis mt-1">
|
<div class="text-caption text-medium-emphasis mt-1">
|
||||||
搜索 <span class="primary-text font-weight-medium">{{ searchWord }}</span> 相关资源
|
{{ t('common.search') }} <span class="primary-text font-weight-medium">{{ searchWord }}</span>
|
||||||
|
{{ t('dialog.searchBar.relatedResources') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<VBtn
|
<VBtn
|
||||||
@@ -584,7 +607,7 @@ onMounted(() => {
|
|||||||
variant="flat"
|
variant="flat"
|
||||||
class="search-btn"
|
class="search-btn"
|
||||||
>
|
>
|
||||||
搜索
|
{{ t('common.search') }}
|
||||||
</VBtn>
|
</VBtn>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -628,7 +651,7 @@ onMounted(() => {
|
|||||||
class="ml-auto site-select-btn"
|
class="ml-auto site-select-btn"
|
||||||
rounded="pill"
|
rounded="pill"
|
||||||
>
|
>
|
||||||
选择站点
|
{{ t('dialog.searchBar.selectSites') }}
|
||||||
<VIcon size="small" class="ml-1">mdi-cog-outline</VIcon>
|
<VIcon size="small" class="ml-1">mdi-cog-outline</VIcon>
|
||||||
</VBtn>
|
</VBtn>
|
||||||
</div>
|
</div>
|
||||||
@@ -641,7 +664,7 @@ onMounted(() => {
|
|||||||
<!-- 无搜索词时显示最近搜索和提示 -->
|
<!-- 无搜索词时显示最近搜索和提示 -->
|
||||||
<div v-else class="recent-searches py-6 px-4 px-sm-6">
|
<div v-else class="recent-searches py-6 px-4 px-sm-6">
|
||||||
<div v-if="recentSearches.length > 0" class="mb-6">
|
<div v-if="recentSearches.length > 0" class="mb-6">
|
||||||
<div class="text-h6 font-weight-medium mb-3">最近搜索</div>
|
<div class="text-h6 font-weight-medium mb-3">{{ t('dialog.searchBar.recentSearches') }}</div>
|
||||||
<div class="d-flex flex-wrap">
|
<div class="d-flex flex-wrap">
|
||||||
<VChip
|
<VChip
|
||||||
v-for="(word, index) in recentSearches"
|
v-for="(word, index) in recentSearches"
|
||||||
@@ -658,12 +681,12 @@ onMounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="text-center mt-6 py-6 empty-search-state">
|
<div v-else class="text-center mt-6 py-6 empty-search-state">
|
||||||
<div class="search-icon-wrapper mx-auto mb-4">
|
<div class="search-icon-wrapper mx-auto mb-4">
|
||||||
<VIcon icon="mdi-magnify" size="large" color="primary" />
|
<VIcon icon="mdi-magnify" size="large" color="primary" />
|
||||||
</div>
|
</div>
|
||||||
<div class="text-h6 font-weight-medium mb-2">输入关键词开始搜索</div>
|
<div class="text-h6 font-weight-medium mb-2">{{ t('dialog.searchBar.searchPlaceholder') }}</div>
|
||||||
<div class="text-body-2 text-medium-emphasis">可搜索电影、电视剧、演员、资源等</div>
|
<div class="text-body-2 text-medium-emphasis">{{ t('dialog.searchBar.searchTip') }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</VCardText>
|
</VCardText>
|
||||||
@@ -790,10 +813,10 @@ onMounted(() => {
|
|||||||
|
|
||||||
.empty-search-state,
|
.empty-search-state,
|
||||||
.empty-site-state {
|
.empty-site-state {
|
||||||
animation: fadeIn 0.3s ease-in-out;
|
animation: fade-in 0.3s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes fadeIn {
|
@keyframes fade-in {
|
||||||
from {
|
from {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateY(10px);
|
transform: translateY(10px);
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Site } from '@/api/types'
|
import type { Site } from '@/api/types'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
|
// 多语言支持
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
sites: {
|
sites: {
|
||||||
@@ -29,7 +33,9 @@ watch(
|
|||||||
|
|
||||||
// 全选/全不选按钮文字
|
// 全选/全不选按钮文字
|
||||||
const checkAllText = computed(() => {
|
const checkAllText = computed(() => {
|
||||||
return selectedSites.value.length < props.sites?.length ? '选择全部' : '取消全选'
|
return selectedSites.value.length < props.sites?.length
|
||||||
|
? t('dialog.searchSite.selectAll')
|
||||||
|
: t('dialog.searchSite.deselectAll')
|
||||||
})
|
})
|
||||||
|
|
||||||
// 全选/全不选
|
// 全选/全不选
|
||||||
@@ -49,15 +55,15 @@ const filteredSites = computed(() => {
|
|||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<!-- 手动整理进度框 -->
|
<!-- Site Selection Dialog -->
|
||||||
<VDialog max-width="40rem" fullscreen-mobile>
|
<VDialog max-width="40rem" fullscreen-mobile>
|
||||||
<VCard class="site-dialog">
|
<VCard class="site-dialog">
|
||||||
<VCardTitle class="d-flex align-center pa-4">
|
<VCardTitle class="d-flex align-center pa-4">
|
||||||
<span class="text-h6 font-weight-medium">选择搜索站点</span>
|
<span class="text-h6 font-weight-medium">{{ t('dialog.searchSite.selectSites') }}</span>
|
||||||
<VSpacer />
|
<VSpacer />
|
||||||
<VTextField
|
<VTextField
|
||||||
v-model="siteFilter"
|
v-model="siteFilter"
|
||||||
placeholder="过滤站点..."
|
:placeholder="t('dialog.searchSite.siteSearch')"
|
||||||
density="compact"
|
density="compact"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
hide-details
|
hide-details
|
||||||
@@ -91,7 +97,7 @@ const filteredSites = computed(() => {
|
|||||||
class="text-body-2 font-weight-medium"
|
class="text-body-2 font-weight-medium"
|
||||||
:class="selectedSites.length > 0 ? 'text-primary' : 'text-medium-emphasis'"
|
:class="selectedSites.length > 0 ? 'text-primary' : 'text-medium-emphasis'"
|
||||||
>
|
>
|
||||||
已选择 {{ selectedSites.length }}/{{ sites.length }} 个站点
|
{{ t('dialog.searchSite.searchAllSites', { selected: selectedSites.length, total: sites.length }) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -137,9 +143,9 @@ const filteredSites = computed(() => {
|
|||||||
<div class="search-icon-wrapper mb-4 mx-auto warning">
|
<div class="search-icon-wrapper mb-4 mx-auto warning">
|
||||||
<VIcon icon="mdi-alert-circle-outline" size="large" color="warning" />
|
<VIcon icon="mdi-alert-circle-outline" size="large" color="warning" />
|
||||||
</div>
|
</div>
|
||||||
<div class="text-h6 font-weight-medium mb-2">没有找到匹配的站点</div>
|
<div class="text-h6 font-weight-medium mb-2">{{ t('torrent.noMatchingResults') }}</div>
|
||||||
<div class="text-subtitle-1 text-medium-emphasis mb-4">
|
<div class="text-subtitle-1 text-medium-emphasis mb-4">
|
||||||
{{ siteFilter ? '请尝试修改过滤条件' : '站点数据加载失败,请刷新页面重试' }}
|
{{ siteFilter ? t('site.noFilterData') : t('site.sitesWillBeShownHere') }}
|
||||||
</div>
|
</div>
|
||||||
<VBtn
|
<VBtn
|
||||||
v-if="siteFilter"
|
v-if="siteFilter"
|
||||||
@@ -149,10 +155,10 @@ const filteredSites = computed(() => {
|
|||||||
prepend-icon="mdi-refresh"
|
prepend-icon="mdi-refresh"
|
||||||
@click="siteFilter = ''"
|
@click="siteFilter = ''"
|
||||||
>
|
>
|
||||||
重置
|
{{ t('torrent.clearFilters') }}
|
||||||
</VBtn>
|
</VBtn>
|
||||||
<VBtn v-else color="primary" variant="flat" class="mt-3" prepend-icon="mdi-refresh" @click="emit('reload')">
|
<VBtn v-else color="primary" variant="flat" class="mt-3" prepend-icon="mdi-refresh" @click="emit('reload')">
|
||||||
重新加载站点
|
{{ t('common.loading') }}
|
||||||
</VBtn>
|
</VBtn>
|
||||||
</div>
|
</div>
|
||||||
</VCardText>
|
</VCardText>
|
||||||
@@ -167,7 +173,7 @@ const filteredSites = computed(() => {
|
|||||||
@click="emit('close')"
|
@click="emit('close')"
|
||||||
class="mr-2 d-flex align-center justify-center"
|
class="mr-2 d-flex align-center justify-center"
|
||||||
>
|
>
|
||||||
取消
|
{{ t('dialog.searchSite.cancel') }}
|
||||||
</VBtn>
|
</VBtn>
|
||||||
<VBtn
|
<VBtn
|
||||||
color="primary"
|
color="primary"
|
||||||
@@ -177,7 +183,7 @@ const filteredSites = computed(() => {
|
|||||||
prepend-icon="mdi-magnify"
|
prepend-icon="mdi-magnify"
|
||||||
class="d-flex align-center justify-center px-5"
|
class="d-flex align-center justify-center px-5"
|
||||||
>
|
>
|
||||||
搜索
|
{{ t('dialog.searchSite.confirm') }}
|
||||||
</VBtn>
|
</VBtn>
|
||||||
</VCardActions>
|
</VCardActions>
|
||||||
</VCard>
|
</VCard>
|
||||||
|
|||||||
@@ -5,6 +5,10 @@ import api from '@/api'
|
|||||||
import type { Subscribe, SubscribeShare } from '@/api/types'
|
import type { Subscribe, SubscribeShare } from '@/api/types'
|
||||||
import { useDisplay } from 'vuetify'
|
import { useDisplay } from 'vuetify'
|
||||||
import { formatSeason } from '@/@core/utils/formatters'
|
import { formatSeason } from '@/@core/utils/formatters'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
|
// 多语言支持
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
// 显示器宽度
|
// 显示器宽度
|
||||||
const display = useDisplay()
|
const display = useDisplay()
|
||||||
@@ -35,11 +39,11 @@ async function doShare() {
|
|||||||
shareDoing.value = false
|
shareDoing.value = false
|
||||||
// 提示
|
// 提示
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
$toast.success(`${props.sub?.name} 分享成功!`)
|
$toast.success(t('dialog.subscribeShare.shareSuccess', { name: props.sub?.name }))
|
||||||
// 通知父组件刷新
|
// 通知父组件刷新
|
||||||
emit('close')
|
emit('close')
|
||||||
} else {
|
} else {
|
||||||
$toast.error(`${props.sub?.name} 分享失败:${result.message}!`)
|
$toast.error(t('dialog.subscribeShare.shareFailed', { name: props.sub?.name, message: result.message }))
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e)
|
console.log(e)
|
||||||
@@ -53,7 +57,9 @@ const $toast = useToast()
|
|||||||
<template>
|
<template>
|
||||||
<VDialog scrollable max-width="30rem" :fullscreen="!display.mdAndUp.value">
|
<VDialog scrollable max-width="30rem" :fullscreen="!display.mdAndUp.value">
|
||||||
<VCard
|
<VCard
|
||||||
:title="`分享订阅 - ${props.sub?.name} ${props.sub?.season ? `第 ${props.sub?.season} 季` : ''}`"
|
:title="`${t('dialog.subscribeShare.shareSubscription')} - ${props.sub?.name} ${
|
||||||
|
props.sub?.season ? t('dialog.subscribeShare.season', { number: props.sub?.season }) : ''
|
||||||
|
}`"
|
||||||
class="rounded-t"
|
class="rounded-t"
|
||||||
>
|
>
|
||||||
<VCardText>
|
<VCardText>
|
||||||
@@ -64,7 +70,7 @@ const $toast = useToast()
|
|||||||
<VTextField
|
<VTextField
|
||||||
v-model="shareForm.share_title"
|
v-model="shareForm.share_title"
|
||||||
readonly
|
readonly
|
||||||
label="标题"
|
:label="t('dialog.subscribeShare.title')"
|
||||||
:rules="[requiredValidator]"
|
:rules="[requiredValidator]"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
/>
|
/>
|
||||||
@@ -72,18 +78,18 @@ const $toast = useToast()
|
|||||||
<VCol cols="12">
|
<VCol cols="12">
|
||||||
<VTextarea
|
<VTextarea
|
||||||
v-model="shareForm.share_comment"
|
v-model="shareForm.share_comment"
|
||||||
label="说明"
|
:label="t('dialog.subscribeShare.description')"
|
||||||
:rules="[requiredValidator]"
|
:rules="[requiredValidator]"
|
||||||
hint="填写关于该订阅的说明,订阅中的搜索词、识别词等将会默认包含在分享中"
|
:hint="t('dialog.subscribeShare.descriptionHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12">
|
<VCol cols="12">
|
||||||
<VTextField
|
<VTextField
|
||||||
v-model="shareForm.share_user"
|
v-model="shareForm.share_user"
|
||||||
label="分享用户"
|
:label="t('dialog.subscribeShare.shareUser')"
|
||||||
:rules="[requiredValidator]"
|
:rules="[requiredValidator]"
|
||||||
hint="分享人的昵称"
|
:hint="t('dialog.subscribeShare.shareUserHint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
@@ -100,7 +106,7 @@ const $toast = useToast()
|
|||||||
class="px-5"
|
class="px-5"
|
||||||
:loading="shareDoing"
|
:loading="shareDoing"
|
||||||
>
|
>
|
||||||
确认分享
|
{{ t('dialog.subscribeShare.confirmShare') }}
|
||||||
</VBtn>
|
</VBtn>
|
||||||
</VCardActions>
|
</VCardActions>
|
||||||
</VCard>
|
</VCard>
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import api from '@/api'
|
import api from '@/api'
|
||||||
import QrcodeVue from 'qrcode.vue'
|
import QrcodeVue from 'qrcode.vue'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
|
// 多语言支持
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
// 定义输入
|
// 定义输入
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -17,7 +21,7 @@ const emit = defineEmits(['done', 'close'])
|
|||||||
const qrCodeContent = ref('')
|
const qrCodeContent = ref('')
|
||||||
|
|
||||||
// 下方的提示信息
|
// 下方的提示信息
|
||||||
const text = ref('请使用微信或115客户端扫码')
|
const text = ref(t('dialog.u115Auth.scanQrCode'))
|
||||||
|
|
||||||
// 提醒类型
|
// 提醒类型
|
||||||
const alertType = ref<'success' | 'info' | 'error' | 'warning' | undefined>('info')
|
const alertType = ref<'success' | 'info' | 'error' | 'warning' | undefined>('info')
|
||||||
@@ -61,7 +65,7 @@ async function checkQrcode() {
|
|||||||
} else if (status == 1) {
|
} else if (status == 1) {
|
||||||
// 已扫码
|
// 已扫码
|
||||||
alertType.value = 'info'
|
alertType.value = 'info'
|
||||||
text.value = '已扫码,请确认登录'
|
text.value = t('dialog.u115Auth.scanned')
|
||||||
clearTimeout(timeoutTimer)
|
clearTimeout(timeoutTimer)
|
||||||
timeoutTimer = setTimeout(checkQrcode, 3000)
|
timeoutTimer = setTimeout(checkQrcode, 3000)
|
||||||
} else if (status == 2) {
|
} else if (status == 2) {
|
||||||
@@ -92,7 +96,7 @@ onUnmounted(() => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<VDialog width="40rem" scrollable max-height="85vh">
|
<VDialog width="40rem" scrollable max-height="85vh">
|
||||||
<VCard title="115网盘登录" class="rounded-t">
|
<VCard :title="t('dialog.u115Auth.loginTitle')" class="rounded-t">
|
||||||
<VDialogCloseBtn @click="emit('close')" />
|
<VDialogCloseBtn @click="emit('close')" />
|
||||||
<VCardText class="pt-2 flex flex-col items-center">
|
<VCardText class="pt-2 flex flex-col items-center">
|
||||||
<div class="my-6 rounded text-center p-3 border">
|
<div class="my-6 rounded text-center p-3 border">
|
||||||
@@ -104,7 +108,9 @@ onUnmounted(() => {
|
|||||||
</VCardText>
|
</VCardText>
|
||||||
<VCardActions>
|
<VCardActions>
|
||||||
<VSpacer />
|
<VSpacer />
|
||||||
<VBtn variant="elevated" @click="handleDone" prepend-icon="mdi-check" class="px-5 me-3"> 完成 </VBtn>
|
<VBtn variant="elevated" @click="handleDone" prepend-icon="mdi-check" class="px-5 me-3">
|
||||||
|
{{ t('dialog.u115Auth.complete') }}
|
||||||
|
</VBtn>
|
||||||
</VCardActions>
|
</VCardActions>
|
||||||
</VCard>
|
</VCard>
|
||||||
</VDialog>
|
</VDialog>
|
||||||
|
|||||||
@@ -6,6 +6,10 @@ import api from '@/api'
|
|||||||
import { useDisplay } from 'vuetify'
|
import { useDisplay } from 'vuetify'
|
||||||
import avatar1 from '@images/avatars/avatar-1.png'
|
import avatar1 from '@images/avatars/avatar-1.png'
|
||||||
import { useUserStore } from '@/stores'
|
import { useUserStore } from '@/stores'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
|
// 多语言支持
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
// 显示器宽度
|
// 显示器宽度
|
||||||
const display = useDisplay()
|
const display = useDisplay()
|
||||||
@@ -52,8 +56,8 @@ const $toast = useToast()
|
|||||||
|
|
||||||
// 状态下拉项
|
// 状态下拉项
|
||||||
const statusItems = [
|
const statusItems = [
|
||||||
{ title: '激活', value: 1 },
|
{ title: t('dialog.userAddEdit.active'), value: 1 },
|
||||||
{ title: '已停用', value: 0 },
|
{ title: t('dialog.userAddEdit.inactive'), value: 0 },
|
||||||
]
|
]
|
||||||
|
|
||||||
// 扩展User类型以包含note字段
|
// 扩展User类型以包含note字段
|
||||||
@@ -92,19 +96,19 @@ function changeAvatar(file: Event) {
|
|||||||
const maxSize = 800 * 1024
|
const maxSize = 800 * 1024
|
||||||
// 检查文件是否为图片
|
// 检查文件是否为图片
|
||||||
if (!allowedTypes.includes(selectedFile.type)) {
|
if (!allowedTypes.includes(selectedFile.type)) {
|
||||||
$toast.error('上传的文件不符合要求,请重新选择头像')
|
$toast.error(t('dialog.userAddEdit.invalidFile'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 检查文件大小
|
// 检查文件大小
|
||||||
if (selectedFile.size > maxSize) {
|
if (selectedFile.size > maxSize) {
|
||||||
$toast.error('文件大小不得大于800KB')
|
$toast.error(t('dialog.userAddEdit.fileSizeLimit'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fileReader.readAsDataURL(selectedFile)
|
fileReader.readAsDataURL(selectedFile)
|
||||||
fileReader.onload = () => {
|
fileReader.onload = () => {
|
||||||
if (typeof fileReader.result === 'string') {
|
if (typeof fileReader.result === 'string') {
|
||||||
currentAvatar.value = fileReader.result
|
currentAvatar.value = fileReader.result
|
||||||
$toast.success('新头像上传成功,待保存后生效!')
|
$toast.success(t('dialog.userAddEdit.avatarUploadSuccess'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -113,13 +117,13 @@ function changeAvatar(file: Event) {
|
|||||||
// 重置默认头像
|
// 重置默认头像
|
||||||
function resetDefaultAvatar() {
|
function resetDefaultAvatar() {
|
||||||
currentAvatar.value = avatar1
|
currentAvatar.value = avatar1
|
||||||
$toast.success('已重置为默认头像,待保存后生效!')
|
$toast.success(t('dialog.userAddEdit.resetAvatarSuccess'))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 还原当前头像
|
// 还原当前头像
|
||||||
function restoreCurrentAvatar() {
|
function restoreCurrentAvatar() {
|
||||||
currentAvatar.value = userForm.value.avatar
|
currentAvatar.value = userForm.value.avatar
|
||||||
$toast.success('已还原当前使用头像!')
|
$toast.success(t('dialog.userAddEdit.restoreAvatarSuccess'))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询用户信息
|
// 查询用户信息
|
||||||
@@ -140,22 +144,22 @@ async function fetchUserInfo() {
|
|||||||
// 调用API 新增用户
|
// 调用API 新增用户
|
||||||
async function addUser() {
|
async function addUser() {
|
||||||
if (isAdding.value) {
|
if (isAdding.value) {
|
||||||
$toast.error(`正在创建【${userForm.value.name}】用户,请稍后`)
|
$toast.error(t('dialog.userAddEdit.creatingUser', { name: userForm.value.name }))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!currentUserName.value) {
|
if (!currentUserName.value) {
|
||||||
$toast.error('用户名不能为空')
|
$toast.error(t('dialog.userAddEdit.usernameRequired'))
|
||||||
return
|
return
|
||||||
} else userForm.value.name = currentUserName.value
|
} else userForm.value.name = currentUserName.value
|
||||||
// 重名检查
|
// 重名检查
|
||||||
if (props.usernames && props.usernames.includes(userForm.value.name)) {
|
if (props.usernames && props.usernames.includes(userForm.value.name)) {
|
||||||
$toast.error('用户名已存在')
|
$toast.error(t('dialog.userAddEdit.usernameExists'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!userForm.value?.name || !newPassword.value) return
|
if (!userForm.value?.name || !newPassword.value) return
|
||||||
if (newPassword.value || confirmPassword.value) {
|
if (newPassword.value || confirmPassword.value) {
|
||||||
if (newPassword.value !== confirmPassword.value) {
|
if (newPassword.value !== confirmPassword.value) {
|
||||||
$toast.error('两次输入的密码不一致')
|
$toast.error(t('dialog.userAddEdit.passwordMismatch'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
userForm.value.password = newPassword.value
|
userForm.value.password = newPassword.value
|
||||||
@@ -165,10 +169,10 @@ async function addUser() {
|
|||||||
try {
|
try {
|
||||||
const result: { [key: string]: string } = await api.post('user/', userForm.value)
|
const result: { [key: string]: string } = await api.post('user/', userForm.value)
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
$toast.success(`用户【${userForm.value.name}】创建成功`)
|
$toast.success(t('dialog.userAddEdit.userCreated', { name: userForm.value.name }))
|
||||||
emit('save')
|
emit('save')
|
||||||
} else {
|
} else {
|
||||||
$toast.error(`创建用户失败:${result.message}`)
|
$toast.error(t('dialog.userAddEdit.userCreateFailed', { message: result.message }))
|
||||||
// 清除用户名
|
// 清除用户名
|
||||||
userForm.value.name = ''
|
userForm.value.name = ''
|
||||||
}
|
}
|
||||||
@@ -182,16 +186,16 @@ async function addUser() {
|
|||||||
// 调用API更新用户信息
|
// 调用API更新用户信息
|
||||||
async function updateUser() {
|
async function updateUser() {
|
||||||
if (isUpdating.value) {
|
if (isUpdating.value) {
|
||||||
$toast.error(`正在更新【${userForm.value.name}】用户,请稍后`)
|
$toast.error(t('dialog.userAddEdit.updatingUser', { name: userForm.value.name }))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!currentUserName.value) {
|
if (!currentUserName.value) {
|
||||||
$toast.error('用户名不能为空')
|
$toast.error(t('dialog.userAddEdit.usernameRequired'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (newPassword.value || confirmPassword.value) {
|
if (newPassword.value || confirmPassword.value) {
|
||||||
if (newPassword.value !== confirmPassword.value) {
|
if (newPassword.value !== confirmPassword.value) {
|
||||||
$toast.error('两次输入的密码不一致')
|
$toast.error(t('dialog.userAddEdit.passwordMismatch'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
userForm.value.password = newPassword.value
|
userForm.value.password = newPassword.value
|
||||||
@@ -219,13 +223,13 @@ async function updateUser() {
|
|||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
if (oldUserName !== currentUserName.value) {
|
if (oldUserName !== currentUserName.value) {
|
||||||
$toast.success(`【${oldUserName}】更名【${currentUserName.value}】, 更新成功!`)
|
$toast.success(t('dialog.userAddEdit.userUpdateSuccess', { name: `${oldUserName} → ${currentUserName.value}` }))
|
||||||
// 如果是当前登录用户,更新当前用户名称显示
|
// 如果是当前登录用户,更新当前用户名称显示
|
||||||
if (isCurrentUser.value) {
|
if (isCurrentUser.value) {
|
||||||
userStore.setUserName(currentUserName.value)
|
userStore.setUserName(currentUserName.value)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$toast.success(`【${userForm.value?.name}】更新成功!`)
|
$toast.success(t('dialog.userAddEdit.userUpdateSuccess', { name: userForm.value?.name }))
|
||||||
}
|
}
|
||||||
// 更新本地头像显示
|
// 更新本地头像显示
|
||||||
if (oldAvatar !== currentAvatar.value && isCurrentUser.value) {
|
if (oldAvatar !== currentAvatar.value && isCurrentUser.value) {
|
||||||
@@ -234,10 +238,10 @@ async function updateUser() {
|
|||||||
emit('save')
|
emit('save')
|
||||||
} else {
|
} else {
|
||||||
if (oldUserName !== currentUserName.value) {
|
if (oldUserName !== currentUserName.value) {
|
||||||
$toast.error(`【${oldUserName}】更名【${currentUserName.value}】, 更新失败:${result.message}`)
|
$toast.error(t('dialog.userAddEdit.userUpdateFailed', { message: result.message }))
|
||||||
currentUserName.value = oldUserName
|
currentUserName.value = oldUserName
|
||||||
} else {
|
} else {
|
||||||
$toast.error(`【${userForm.value?.name}】更新失败:${result.message}`)
|
$toast.error(t('dialog.userAddEdit.userUpdateFailed', { message: result.message }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//失败缓存值还原
|
//失败缓存值还原
|
||||||
@@ -247,7 +251,7 @@ async function updateUser() {
|
|||||||
userForm.value.avatar = oldAvatar
|
userForm.value.avatar = oldAvatar
|
||||||
userForm.value.password = ''
|
userForm.value.password = ''
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
$toast.error(`【${userForm.value?.name}】更新失败!`)
|
$toast.error(t('dialog.userAddEdit.userUpdateFailed', { message: '' }))
|
||||||
console.error('更新失败:', error)
|
console.error('更新失败:', error)
|
||||||
}
|
}
|
||||||
doneNProgress()
|
doneNProgress()
|
||||||
@@ -288,7 +292,9 @@ onMounted(() => {
|
|||||||
<template>
|
<template>
|
||||||
<VDialog scrollable :close-on-back="false" eager max-width="40rem" :fullscreen="!display.mdAndUp.value">
|
<VDialog scrollable :close-on-back="false" eager max-width="40rem" :fullscreen="!display.mdAndUp.value">
|
||||||
<VCard
|
<VCard
|
||||||
:title="`${props.oper === 'add' ? '新增' : '编辑'}用户${props.oper !== 'add' ? ` - ${userName}` : ''}`"
|
:title="`${props.oper === 'add' ? t('dialog.userAddEdit.add') : t('dialog.userAddEdit.edit')}${
|
||||||
|
props.oper !== 'add' ? ` - ${userName}` : ''
|
||||||
|
}`"
|
||||||
class="rounded-t"
|
class="rounded-t"
|
||||||
>
|
>
|
||||||
<VDialogCloseBtn @click="emit('close')" />
|
<VDialogCloseBtn @click="emit('close')" />
|
||||||
@@ -302,7 +308,7 @@ onMounted(() => {
|
|||||||
<div class="flex flex-wrap gap-2">
|
<div class="flex flex-wrap gap-2">
|
||||||
<VBtn color="primary" @click="refInputEl?.click()">
|
<VBtn color="primary" @click="refInputEl?.click()">
|
||||||
<VIcon icon="mdi-cloud-upload-outline" />
|
<VIcon icon="mdi-cloud-upload-outline" />
|
||||||
<span v-if="display.mdAndUp.value" class="ms-2">上传新头像</span>
|
<span v-if="display.mdAndUp.value" class="ms-2">{{ t('dialog.userAddEdit.uploadAvatar') }}</span>
|
||||||
</VBtn>
|
</VBtn>
|
||||||
|
|
||||||
<input
|
<input
|
||||||
@@ -316,7 +322,7 @@ onMounted(() => {
|
|||||||
|
|
||||||
<VBtn type="reset" color="info" variant="tonal" @click="restoreCurrentAvatar" v-if="props.oper !== 'add'">
|
<VBtn type="reset" color="info" variant="tonal" @click="restoreCurrentAvatar" v-if="props.oper !== 'add'">
|
||||||
<VIcon icon="mdi-refresh" />
|
<VIcon icon="mdi-refresh" />
|
||||||
<span v-if="display.mdAndUp.value" class="ms-2">重置</span>
|
<span v-if="display.mdAndUp.value" class="ms-2">{{ t('common.cancel') }}</span>
|
||||||
</VBtn>
|
</VBtn>
|
||||||
|
|
||||||
<VBtn
|
<VBtn
|
||||||
@@ -326,17 +332,17 @@ onMounted(() => {
|
|||||||
@click="resetDefaultAvatar"
|
@click="resetDefaultAvatar"
|
||||||
>
|
>
|
||||||
<VIcon icon="mdi-image-sync-outline" />
|
<VIcon icon="mdi-image-sync-outline" />
|
||||||
<span v-if="display.mdAndUp.value" class="ms-2">默认</span>
|
<span v-if="display.mdAndUp.value" class="ms-2">{{ t('dialog.userAddEdit.resetDefaultAvatar') }}</span>
|
||||||
</VBtn>
|
</VBtn>
|
||||||
</div>
|
</div>
|
||||||
<p class="text-body-1 mb-0">允许 JPG、PNG、GIF、WEBP 格式, 最大尺寸 800KB。</p>
|
<p class="text-body-1 mb-0">{{ t('dialog.userAddEdit.fileSizeLimit') }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</VCardItem>
|
</VCardItem>
|
||||||
<VCardText>
|
<VCardText>
|
||||||
<VForm @submit.prevent="() => {}">
|
<VForm @submit.prevent="() => {}">
|
||||||
<VDivider class="my-10">
|
<VDivider class="my-10">
|
||||||
<span>用户基础设置</span>
|
<span>{{ t('dialog.userAddEdit.saveUserInfo') }}</span>
|
||||||
</VDivider>
|
</VDivider>
|
||||||
<VRow>
|
<VRow>
|
||||||
<VCol md="6" cols="12">
|
<VCol md="6" cols="12">
|
||||||
@@ -344,11 +350,17 @@ onMounted(() => {
|
|||||||
v-model="currentUserName"
|
v-model="currentUserName"
|
||||||
density="comfortable"
|
density="comfortable"
|
||||||
:readonly="props.oper !== 'add'"
|
:readonly="props.oper !== 'add'"
|
||||||
label="用户名"
|
:label="t('dialog.userAddEdit.username')"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
<VTextField v-model="userForm.email" density="comfortable" clearable label="邮箱" type="email" />
|
<VTextField
|
||||||
|
v-model="userForm.email"
|
||||||
|
density="comfortable"
|
||||||
|
clearable
|
||||||
|
:label="t('dialog.userAddEdit.email')"
|
||||||
|
type="email"
|
||||||
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
<VTextField
|
<VTextField
|
||||||
@@ -357,7 +369,7 @@ onMounted(() => {
|
|||||||
:type="isNewPasswordVisible ? 'text' : 'password'"
|
:type="isNewPasswordVisible ? 'text' : 'password'"
|
||||||
:append-inner-icon="isNewPasswordVisible ? 'mdi-eye-off-outline' : 'mdi-eye-outline'"
|
:append-inner-icon="isNewPasswordVisible ? 'mdi-eye-off-outline' : 'mdi-eye-outline'"
|
||||||
clearable
|
clearable
|
||||||
label="密码"
|
:label="t('dialog.userAddEdit.password')"
|
||||||
autocomplete=""
|
autocomplete=""
|
||||||
@click:append-inner="isNewPasswordVisible = !isNewPasswordVisible"
|
@click:append-inner="isNewPasswordVisible = !isNewPasswordVisible"
|
||||||
/>
|
/>
|
||||||
@@ -370,7 +382,7 @@ onMounted(() => {
|
|||||||
:type="isConfirmPasswordVisible ? 'text' : 'password'"
|
:type="isConfirmPasswordVisible ? 'text' : 'password'"
|
||||||
:append-inner-icon="isConfirmPasswordVisible ? 'mdi-eye-off-outline' : 'mdi-eye-outline'"
|
:append-inner-icon="isConfirmPasswordVisible ? 'mdi-eye-off-outline' : 'mdi-eye-outline'"
|
||||||
clearable
|
clearable
|
||||||
label="确认密码"
|
:label="t('dialog.userAddEdit.confirmPassword')"
|
||||||
@click:append-inner="isConfirmPasswordVisible = !isConfirmPasswordVisible"
|
@click:append-inner="isConfirmPasswordVisible = !isConfirmPasswordVisible"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
@@ -379,7 +391,7 @@ onMounted(() => {
|
|||||||
v-model="userForm.nickname"
|
v-model="userForm.nickname"
|
||||||
density="comfortable"
|
density="comfortable"
|
||||||
clearable
|
clearable
|
||||||
label="昵称"
|
:label="t('dialog.userAddEdit.nickname')"
|
||||||
placeholder="显示昵称,优先于用户名显示"
|
placeholder="显示昵称,优先于用户名显示"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
@@ -389,35 +401,45 @@ onMounted(() => {
|
|||||||
:items="statusItems"
|
:items="statusItems"
|
||||||
item-text="title"
|
item-text="title"
|
||||||
item-value="value"
|
item-value="value"
|
||||||
label="状态"
|
:label="t('dialog.userAddEdit.status')"
|
||||||
dense
|
dense
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
<VDivider class="my-10">
|
<VDivider class="my-10">
|
||||||
<span>账号绑定</span>
|
<span>{{ t('dialog.userAddEdit.notifications') }}</span>
|
||||||
</VDivider>
|
</VDivider>
|
||||||
<VRow>
|
<VRow>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
<VTextField v-model="userForm.settings.wechat_userid" density="comfortable" clearable label="微信用户" />
|
<VTextField
|
||||||
|
v-model="userForm.settings.wechat_userid"
|
||||||
|
density="comfortable"
|
||||||
|
clearable
|
||||||
|
:label="t('dialog.userAddEdit.wechat')"
|
||||||
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
<VTextField
|
<VTextField
|
||||||
v-model="userForm.settings.telegram_userid"
|
v-model="userForm.settings.telegram_userid"
|
||||||
density="comfortable"
|
density="comfortable"
|
||||||
clearable
|
clearable
|
||||||
label="Telegram用户"
|
:label="t('dialog.userAddEdit.telegram')"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
<VTextField v-model="userForm.settings.slack_userid" density="comfortable" clearable label="Slack用户" />
|
<VTextField
|
||||||
|
v-model="userForm.settings.slack_userid"
|
||||||
|
density="comfortable"
|
||||||
|
clearable
|
||||||
|
:label="t('dialog.userAddEdit.slack')"
|
||||||
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
<VTextField
|
<VTextField
|
||||||
v-model="userForm.settings.vocechat_userid"
|
v-model="userForm.settings.vocechat_userid"
|
||||||
density="comfortable"
|
density="comfortable"
|
||||||
clearable
|
clearable
|
||||||
label="VoceChat用户"
|
:label="t('dialog.userAddEdit.vocechat')"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -425,7 +447,7 @@ onMounted(() => {
|
|||||||
v-model="userForm.settings.synologychat_userid"
|
v-model="userForm.settings.synologychat_userid"
|
||||||
density="comfortable"
|
density="comfortable"
|
||||||
clearable
|
clearable
|
||||||
label="SynologyChat用户"
|
:label="t('dialog.userAddEdit.synologyChat')"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -445,8 +467,8 @@ onMounted(() => {
|
|||||||
prepend-icon="mdi-plus"
|
prepend-icon="mdi-plus"
|
||||||
class="px-5"
|
class="px-5"
|
||||||
>
|
>
|
||||||
<span v-if="isAdding">创建中...</span>
|
<span v-if="isAdding">{{ t('common.loading') }}</span>
|
||||||
<span v-else>创建</span>
|
<span v-else>{{ t('common.add') }}</span>
|
||||||
</VBtn>
|
</VBtn>
|
||||||
<VBtn
|
<VBtn
|
||||||
v-else
|
v-else
|
||||||
@@ -457,8 +479,8 @@ onMounted(() => {
|
|||||||
prepend-icon="mdi-content-save"
|
prepend-icon="mdi-content-save"
|
||||||
class="px-5"
|
class="px-5"
|
||||||
>
|
>
|
||||||
<span v-if="isUpdating">更新中...</span>
|
<span v-if="isUpdating">{{ t('common.loading') }}</span>
|
||||||
<span v-else>更新</span>
|
<span v-else>{{ t('common.save') }}</span>
|
||||||
</VBtn>
|
</VBtn>
|
||||||
</VCardActions>
|
</VCardActions>
|
||||||
</VCard>
|
</VCard>
|
||||||
|
|||||||
@@ -721,4 +721,119 @@ export default {
|
|||||||
settingsSaveFailed: 'Failed to save subscription basic settings!',
|
settingsSaveFailed: 'Failed to save subscription basic settings!',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
dialog: {
|
||||||
|
progress: {
|
||||||
|
processing: 'Processing',
|
||||||
|
},
|
||||||
|
userAddEdit: {
|
||||||
|
add: 'Add User',
|
||||||
|
edit: 'Edit User',
|
||||||
|
username: 'Username',
|
||||||
|
password: 'Password',
|
||||||
|
confirmPassword: 'Confirm Password',
|
||||||
|
email: 'Email',
|
||||||
|
nickname: 'Nickname',
|
||||||
|
status: 'Status',
|
||||||
|
active: 'Active',
|
||||||
|
inactive: 'Inactive',
|
||||||
|
superUser: 'Super User',
|
||||||
|
otp: 'Enable Two-Factor Authentication',
|
||||||
|
avatar: 'Avatar',
|
||||||
|
uploadAvatar: 'Upload Avatar',
|
||||||
|
resetDefaultAvatar: 'Reset to Default Avatar',
|
||||||
|
restoreCurrentAvatar: 'Restore Current Avatar',
|
||||||
|
notifications: 'Notifications',
|
||||||
|
wechat: 'WeChat UserID',
|
||||||
|
telegram: 'Telegram UserID',
|
||||||
|
slack: 'Slack UserID',
|
||||||
|
vocechat: 'VoceChat UserID',
|
||||||
|
synologyChat: 'SynologyChat UserID',
|
||||||
|
webPush: 'WebPush',
|
||||||
|
creatingUser: 'Creating user [{name}], please wait',
|
||||||
|
updatingUser: 'Updating user [{name}], please wait',
|
||||||
|
usernameRequired: 'Username cannot be empty',
|
||||||
|
usernameExists: 'Username already exists',
|
||||||
|
passwordMismatch: 'Passwords do not match',
|
||||||
|
userCreated: 'User [{name}] created successfully',
|
||||||
|
userCreateFailed: 'Failed to create user: {message}',
|
||||||
|
userUpdateSuccess: 'User [{name}] updated successfully',
|
||||||
|
userUpdateFailed: 'Failed to update user: {message}',
|
||||||
|
userDeleteSuccess: 'User [{name}] deleted successfully',
|
||||||
|
userDeleteFailed: 'Failed to delete user: {message}',
|
||||||
|
invalidFile: 'The uploaded file does not meet requirements, please select another avatar',
|
||||||
|
fileSizeLimit: 'File size cannot exceed 800KB',
|
||||||
|
avatarUploadSuccess: 'New avatar uploaded successfully, will take effect after saving!',
|
||||||
|
resetAvatarSuccess: 'Reset to default avatar, will take effect after saving!',
|
||||||
|
restoreAvatarSuccess: 'Current avatar restored!',
|
||||||
|
deleteConfirm: 'Confirm delete user [{name}]?',
|
||||||
|
saveUserInfo: 'Save User Information',
|
||||||
|
cannotDeleteCurrentUser: 'Cannot delete the currently logged-in user',
|
||||||
|
deleteUser: 'Delete User',
|
||||||
|
},
|
||||||
|
searchBar: {
|
||||||
|
search: 'Search',
|
||||||
|
searchPlaceholder: 'Search functions, subscriptions, settings...',
|
||||||
|
recentSearches: 'Recent Searches',
|
||||||
|
noRecentSearches: 'No recent searches',
|
||||||
|
functions: 'Functions',
|
||||||
|
noFunctionsFound: 'No matching functions',
|
||||||
|
plugins: 'Plugins',
|
||||||
|
noPluginsFound: 'No matching plugins',
|
||||||
|
subscriptions: 'Subscriptions',
|
||||||
|
noSubscriptionsFound: 'No matching subscriptions',
|
||||||
|
searchSites: 'Search Sites',
|
||||||
|
selectSites: 'Select Sites',
|
||||||
|
collections: 'Collections',
|
||||||
|
collectionSearch: 'related collections',
|
||||||
|
actorSearch: 'related actors, directors, etc.',
|
||||||
|
historySearch: 'related history records',
|
||||||
|
siteResources: 'Site Resources',
|
||||||
|
searchInSites: 'Search for torrents in sites',
|
||||||
|
relatedResources: 'related resources',
|
||||||
|
searchTip: 'Search for movies, TV shows, actors, resources, etc.',
|
||||||
|
},
|
||||||
|
searchSite: {
|
||||||
|
selectSites: 'Select Sites',
|
||||||
|
siteSearch: 'Site Search',
|
||||||
|
searchAllSites: 'Selected {selected}/{total} sites',
|
||||||
|
selectAll: 'Select All',
|
||||||
|
deselectAll: 'Deselect All',
|
||||||
|
confirm: 'Confirm',
|
||||||
|
cancel: 'Cancel',
|
||||||
|
},
|
||||||
|
importCode: {
|
||||||
|
import: 'Import',
|
||||||
|
title: 'Import Code',
|
||||||
|
},
|
||||||
|
addDownload: {
|
||||||
|
confirmDownload: 'Confirm Download',
|
||||||
|
downloader: 'Downloader (Default)',
|
||||||
|
saveDirectory: 'Save Directory (Auto)',
|
||||||
|
defaultPlaceholder: 'Leave empty for default',
|
||||||
|
autoPlaceholder: 'Leave empty for auto match',
|
||||||
|
downloading: 'Downloading...',
|
||||||
|
startDownload: 'Start Download',
|
||||||
|
downloadSuccess: '{site} {title} downloaded successfully!',
|
||||||
|
downloadFailed: '{site} {title} download failed: {message}!',
|
||||||
|
},
|
||||||
|
subscribeShare: {
|
||||||
|
shareSubscription: 'Share Subscription',
|
||||||
|
season: 'Season {number}',
|
||||||
|
title: 'Title',
|
||||||
|
description: 'Description',
|
||||||
|
descriptionHint:
|
||||||
|
'Fill in the description of the subscription. Search terms and identification terms in the subscription will be included in the share by default',
|
||||||
|
shareUser: 'Share User',
|
||||||
|
shareUserHint: 'Nickname of the sharer',
|
||||||
|
confirmShare: 'Confirm Share',
|
||||||
|
shareSuccess: '{name} shared successfully!',
|
||||||
|
shareFailed: '{name} share failed: {message}!',
|
||||||
|
},
|
||||||
|
u115Auth: {
|
||||||
|
loginTitle: '115 Cloud Login',
|
||||||
|
scanQrCode: 'Please scan the QR code with WeChat or 115 client',
|
||||||
|
scanned: 'QR code scanned, please confirm login',
|
||||||
|
complete: 'Complete',
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -707,4 +707,118 @@ export default {
|
|||||||
settingsSaveFailed: '订阅基础设置保存失败!',
|
settingsSaveFailed: '订阅基础设置保存失败!',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
dialog: {
|
||||||
|
progress: {
|
||||||
|
processing: '处理中',
|
||||||
|
},
|
||||||
|
userAddEdit: {
|
||||||
|
add: '添加用户',
|
||||||
|
edit: '编辑用户',
|
||||||
|
username: '用户名',
|
||||||
|
password: '密码',
|
||||||
|
confirmPassword: '确认密码',
|
||||||
|
email: '邮箱',
|
||||||
|
nickname: '昵称',
|
||||||
|
status: '状态',
|
||||||
|
active: '激活',
|
||||||
|
inactive: '已停用',
|
||||||
|
superUser: '超级用户',
|
||||||
|
otp: '启用二次验证',
|
||||||
|
avatar: '头像',
|
||||||
|
uploadAvatar: '上传头像',
|
||||||
|
resetDefaultAvatar: '重置默认头像',
|
||||||
|
restoreCurrentAvatar: '还原当前头像',
|
||||||
|
notifications: '通知',
|
||||||
|
wechat: '微信UserID',
|
||||||
|
telegram: 'Telegram UserID',
|
||||||
|
slack: 'Slack UserID',
|
||||||
|
vocechat: 'VoceChat UserID',
|
||||||
|
synologyChat: 'SynologyChat UserID',
|
||||||
|
webPush: 'WebPush',
|
||||||
|
creatingUser: '正在创建【{name}】用户,请稍后',
|
||||||
|
updatingUser: '正在更新【{name}】用户,请稍后',
|
||||||
|
usernameRequired: '用户名不能为空',
|
||||||
|
usernameExists: '用户名已存在',
|
||||||
|
passwordMismatch: '两次输入的密码不一致',
|
||||||
|
userCreated: '用户【{name}】创建成功',
|
||||||
|
userCreateFailed: '创建用户失败:{message}',
|
||||||
|
userUpdateSuccess: '用户【{name}】更新成功',
|
||||||
|
userUpdateFailed: '更新用户失败:{message}',
|
||||||
|
userDeleteSuccess: '用户【{name}】删除成功',
|
||||||
|
userDeleteFailed: '删除用户失败:{message}',
|
||||||
|
invalidFile: '上传的文件不符合要求,请重新选择头像',
|
||||||
|
fileSizeLimit: '文件大小不得大于800KB',
|
||||||
|
avatarUploadSuccess: '新头像上传成功,待保存后生效!',
|
||||||
|
resetAvatarSuccess: '已重置为默认头像,待保存后生效!',
|
||||||
|
restoreAvatarSuccess: '已还原当前使用头像!',
|
||||||
|
deleteConfirm: '确认删除用户【{name}】吗?',
|
||||||
|
saveUserInfo: '保存用户信息',
|
||||||
|
cannotDeleteCurrentUser: '不能删除当前登录用户',
|
||||||
|
deleteUser: '删除用户',
|
||||||
|
},
|
||||||
|
searchBar: {
|
||||||
|
search: '搜索',
|
||||||
|
searchPlaceholder: '搜索功能、订阅、设置...',
|
||||||
|
recentSearches: '最近搜索',
|
||||||
|
noRecentSearches: '没有最近搜索记录',
|
||||||
|
functions: '功能',
|
||||||
|
noFunctionsFound: '没有匹配的功能',
|
||||||
|
plugins: '插件',
|
||||||
|
noPluginsFound: '没有匹配的插件',
|
||||||
|
subscriptions: '订阅',
|
||||||
|
noSubscriptionsFound: '没有匹配的订阅',
|
||||||
|
searchSites: '搜索站点',
|
||||||
|
selectSites: '选择站点',
|
||||||
|
collections: '系列合集',
|
||||||
|
collectionSearch: '相关的系列作品',
|
||||||
|
actorSearch: '相关的演员、导演等',
|
||||||
|
historySearch: '相关的历史记录',
|
||||||
|
siteResources: '站点资源',
|
||||||
|
searchInSites: '在站点中搜索种子资源',
|
||||||
|
relatedResources: '相关资源',
|
||||||
|
searchTip: '可搜索电影、电视剧、演员、资源等',
|
||||||
|
},
|
||||||
|
searchSite: {
|
||||||
|
selectSites: '选择站点',
|
||||||
|
siteSearch: '站点搜索',
|
||||||
|
searchAllSites: '已选择 {selected}/{total} 个站点',
|
||||||
|
selectAll: '选择全部',
|
||||||
|
deselectAll: '取消全选',
|
||||||
|
confirm: '确认',
|
||||||
|
cancel: '取消',
|
||||||
|
},
|
||||||
|
importCode: {
|
||||||
|
import: '导入',
|
||||||
|
title: '导入代码',
|
||||||
|
},
|
||||||
|
addDownload: {
|
||||||
|
confirmDownload: '确认下载',
|
||||||
|
downloader: '下载器(默认)',
|
||||||
|
saveDirectory: '保存目录(自动)',
|
||||||
|
defaultPlaceholder: '留空默认',
|
||||||
|
autoPlaceholder: '留空自动匹配',
|
||||||
|
downloading: '下载中...',
|
||||||
|
startDownload: '开始下载',
|
||||||
|
downloadSuccess: '{site} {title} 下载成功!',
|
||||||
|
downloadFailed: '{site} {title} 下载失败:{message}!',
|
||||||
|
},
|
||||||
|
subscribeShare: {
|
||||||
|
shareSubscription: '分享订阅',
|
||||||
|
season: '第 {number} 季',
|
||||||
|
title: '标题',
|
||||||
|
description: '说明',
|
||||||
|
descriptionHint: '填写关于该订阅的说明,订阅中的搜索词、识别词等将会默认包含在分享中',
|
||||||
|
shareUser: '分享用户',
|
||||||
|
shareUserHint: '分享人的昵称',
|
||||||
|
confirmShare: '确认分享',
|
||||||
|
shareSuccess: '{name} 分享成功!',
|
||||||
|
shareFailed: '{name} 分享失败:{message}!',
|
||||||
|
},
|
||||||
|
u115Auth: {
|
||||||
|
loginTitle: '115网盘登录',
|
||||||
|
scanQrCode: '请使用微信或115客户端扫码',
|
||||||
|
scanned: '已扫码,请确认登录',
|
||||||
|
complete: '完成',
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user