mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-07-03 13:31:34 +08:00
更新国际化支持:在多个对话框组件中引入 vue-i18n,优化文本翻译,确保多语言显示的一致性和准确性。
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
<script lang="ts" setup>
|
||||
import api from '@/api'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
// 多语言支持
|
||||
const { t } = useI18n()
|
||||
|
||||
// 定义输入
|
||||
const props = defineProps({
|
||||
@@ -30,22 +34,32 @@ async function savaAlistConfig() {
|
||||
|
||||
<template>
|
||||
<VDialog width="50rem" scrollable max-height="85vh">
|
||||
<VCard title="AList配置" class="rounded-t">
|
||||
<VCard :title="t('dialog.alistConfig.title')" class="rounded-t">
|
||||
<VDialogCloseBtn @click="emit('close')" />
|
||||
<VCardText>
|
||||
<VRow>
|
||||
<VCol cols="12">
|
||||
<VTextField v-model="props.conf.url" hint="AList服务地址" label="地址" persistent-hint />
|
||||
<VTextField
|
||||
v-model="props.conf.url"
|
||||
:hint="t('dialog.alistConfig.serverUrl')"
|
||||
:label="t('dialog.alistConfig.serverUrl')"
|
||||
persistent-hint
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField v-model="props.conf.username" hint="AList登录用户名" label="用户名" persistent-hint />
|
||||
<VTextField
|
||||
v-model="props.conf.username"
|
||||
:hint="t('dialog.alistConfig.username')"
|
||||
:label="t('dialog.alistConfig.username')"
|
||||
persistent-hint
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField
|
||||
type="password"
|
||||
v-model="props.conf.password"
|
||||
hint="AList登录密码"
|
||||
label="密码"
|
||||
:hint="t('dialog.alistConfig.password')"
|
||||
:label="t('dialog.alistConfig.password')"
|
||||
persistent-hint
|
||||
/>
|
||||
</VCol>
|
||||
@@ -53,7 +67,9 @@ async function savaAlistConfig() {
|
||||
</VCardText>
|
||||
<VCardActions>
|
||||
<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.alistConfig.complete') }}
|
||||
</VBtn>
|
||||
</VCardActions>
|
||||
</VCard>
|
||||
</VDialog>
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
<script lang="ts" setup>
|
||||
import api from '@/api'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
// 多语言支持
|
||||
const { t } = useI18n()
|
||||
|
||||
// 定义输入
|
||||
defineProps({
|
||||
@@ -16,7 +20,7 @@ const emit = defineEmits(['done', 'close'])
|
||||
const qrCodeUrl = ref('')
|
||||
|
||||
// 下方的提示信息
|
||||
const text = ref('请用阿里云盘 App 扫码')
|
||||
const text = ref(t('dialog.aliyunAuth.scanQrCode'))
|
||||
|
||||
// 提醒类型
|
||||
const alertType = ref<'success' | 'info' | 'error' | 'warning' | undefined>('info')
|
||||
@@ -85,7 +89,7 @@ onUnmounted(() => {
|
||||
|
||||
<template>
|
||||
<VDialog width="40rem" scrollable max-height="85vh">
|
||||
<VCard title="阿里云盘登录" class="rounded-t">
|
||||
<VCard :title="t('dialog.aliyunAuth.loginTitle')" class="rounded-t">
|
||||
<VDialogCloseBtn @click="emit('close')" />
|
||||
<VCardText class="pt-2 flex flex-col items-center">
|
||||
<div class="my-6 rounded text-center p-3 border">
|
||||
@@ -103,7 +107,9 @@ onUnmounted(() => {
|
||||
</VCardText>
|
||||
<VCardActions>
|
||||
<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.aliyunAuth.complete') }}
|
||||
</VBtn>
|
||||
</VCardActions>
|
||||
</VCard>
|
||||
</VDialog>
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
<script lang="ts" setup>
|
||||
import api from '@/api'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
// 多语言支持
|
||||
const { t } = useI18n()
|
||||
|
||||
// 定义输入
|
||||
const props = defineProps({
|
||||
@@ -14,7 +18,7 @@ if (!props.conf.filepath) {
|
||||
}
|
||||
|
||||
if (!props.conf.content) {
|
||||
props.conf.content = '# 请在此处填写rclone配置文件内容 \n# 请参考 https://rclone.org/docs/ \n# 存储节点名必须为:MP'
|
||||
props.conf.content = t('dialog.rcloneConfig.defaultContent')
|
||||
}
|
||||
|
||||
// 定义事件
|
||||
@@ -38,12 +42,12 @@ async function savaRcloneConfig() {
|
||||
|
||||
<template>
|
||||
<VDialog width="50rem" scrollable max-height="85vh">
|
||||
<VCard title="RClone配置" class="rounded-t">
|
||||
<VCard :title="t('dialog.rcloneConfig.title')" class="rounded-t">
|
||||
<VDialogCloseBtn @click="emit('close')" />
|
||||
<VCardText>
|
||||
<VRow>
|
||||
<VCol cols="12">
|
||||
<VTextField v-model="props.conf.filepath" label="rclone配置文件路径" />
|
||||
<VTextField v-model="props.conf.filepath" :label="t('dialog.rcloneConfig.filePath')" />
|
||||
</VCol>
|
||||
<VCol cols="12">
|
||||
<VAceEditor
|
||||
@@ -59,7 +63,9 @@ async function savaRcloneConfig() {
|
||||
</VCardText>
|
||||
<VCardActions>
|
||||
<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.rcloneConfig.complete') }}
|
||||
</VBtn>
|
||||
</VCardActions>
|
||||
</VCard>
|
||||
</VDialog>
|
||||
|
||||
@@ -4,6 +4,10 @@ import api from '@/api'
|
||||
import { useDisplay, useTheme } from 'vuetify'
|
||||
import { formatFileSize } from '@/@core/utils/formatters'
|
||||
import ProgressDialog from '@/components/dialog/ProgressDialog.vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
// 多语言支持
|
||||
const { t } = useI18n()
|
||||
|
||||
// 显示器宽度
|
||||
const display = useDisplay()
|
||||
@@ -36,11 +40,11 @@ const siteData = computed(() => siteDatas.value[siteDatas.value.length - 1])
|
||||
const historySeries = computed(() => {
|
||||
return [
|
||||
{
|
||||
name: '上传量',
|
||||
name: t('dialog.siteUserData.uploadTitle'),
|
||||
data: siteDatas.value.map(item => Math.round((item.upload ?? 0) / 1024 / 1024 / 1024)),
|
||||
},
|
||||
{
|
||||
name: '下载量',
|
||||
name: t('dialog.siteUserData.downloadTitle'),
|
||||
data: siteDatas.value.map(item => Math.round((item.download ?? 0) / 1024 / 1024 / 1024)),
|
||||
},
|
||||
]
|
||||
@@ -135,7 +139,7 @@ const historyChartOptions = computed(() => {
|
||||
const seedingSeries = computed(() => {
|
||||
return [
|
||||
{
|
||||
name: '体积',
|
||||
name: t('dialog.siteUserData.volumeTitle'),
|
||||
data: siteData.value?.seeding_info?.map(item => [item[0] ?? 0, Math.round((item[1] ?? 0) / 1024 / 1024 / 1024)]),
|
||||
},
|
||||
]
|
||||
@@ -162,7 +166,7 @@ const seedingChartOptions = computed(() => {
|
||||
enabled: true,
|
||||
x: {
|
||||
formatter: function (val: number) {
|
||||
return '数量:' + val.toLocaleString()
|
||||
return t('dialog.siteUserData.countTitle') + val.toLocaleString()
|
||||
},
|
||||
},
|
||||
style: {
|
||||
@@ -188,7 +192,7 @@ const seedingChartOptions = computed(() => {
|
||||
},
|
||||
},
|
||||
title: {
|
||||
text: '数量',
|
||||
text: t('dialog.siteUserData.countTitle'),
|
||||
},
|
||||
tickAmount: 10,
|
||||
},
|
||||
@@ -282,7 +286,7 @@ onBeforeMount(async () => {
|
||||
<VCard class="rounded-t">
|
||||
<VCardItem>
|
||||
<VCardTitle
|
||||
>{{ `数据 - ${props.site?.name}` }}
|
||||
>{{ t('dialog.siteUserData.title') }} - {{ props.site?.name }}
|
||||
<IconBtn @click.stop="refreshSiteData" color="info"><VIcon icon="mdi-refresh" /></IconBtn>
|
||||
</VCardTitle>
|
||||
<VDialogCloseBtn @click="emit('close')" />
|
||||
@@ -296,9 +300,9 @@ onBeforeMount(async () => {
|
||||
<VCardText class="d-flex align-center">
|
||||
<div class="d-flex justify-space-between" style="inline-size: 100%">
|
||||
<div class="d-flex flex-column gap-y-1 overflow-hidden">
|
||||
<span class="text-base">用户等级</span>
|
||||
<span class="text-base">{{ t('dialog.siteUserData.userLevel') }}</span>
|
||||
<h5 class="text-h5 d-flex align-center gap-2 text-wrap">
|
||||
{{ siteData?.user_level || '无' }}
|
||||
{{ siteData?.user_level || t('dialog.siteUserData.noData') }}
|
||||
</h5>
|
||||
</div>
|
||||
<VAvatar variant="tonal" size="42" rounded>
|
||||
@@ -314,7 +318,7 @@ onBeforeMount(async () => {
|
||||
<VCardText class="d-flex align-center">
|
||||
<div class="d-flex justify-space-between" style="inline-size: 100%">
|
||||
<div class="d-flex flex-column gap-y-1 overflow-hidden">
|
||||
<span class="text-base">积分</span>
|
||||
<span class="text-base">{{ t('dialog.siteUserData.bonus') }}</span>
|
||||
<h5 class="text-h5 d-flex align-center gap-2 text-wrap">
|
||||
{{ siteData?.bonus?.toLocaleString() }}
|
||||
<span class="text-base font-weight-regular" :class="getDiffClass(diffData?.bonus)">
|
||||
@@ -335,7 +339,7 @@ onBeforeMount(async () => {
|
||||
<VCardText class="d-flex align-center">
|
||||
<div class="d-flex justify-space-between" style="inline-size: 100%">
|
||||
<div class="d-flex flex-column gap-y-1">
|
||||
<span class="text-base">分享率</span>
|
||||
<span class="text-base">{{ t('dialog.siteUserData.ratio') }}</span>
|
||||
<h5 class="text-h5 d-flex align-center gap-2 text-wrap">
|
||||
{{ siteData?.ratio }}
|
||||
<span class="text-base font-weight-regular" :class="getDiffClass(diffData?.ratio)">
|
||||
@@ -356,7 +360,7 @@ onBeforeMount(async () => {
|
||||
<VCardText class="d-flex align-center">
|
||||
<div class="d-flex justify-space-between" style="inline-size: 100%">
|
||||
<div class="d-flex flex-column gap-y-1 overflow-hidden">
|
||||
<span class="text-base">总上传量</span>
|
||||
<span class="text-base">{{ t('dialog.siteUserData.uploadTotal') }}</span>
|
||||
<h5 class="text-h5 d-flex align-center gap-2 text-wrap">
|
||||
{{ formatFileSize(siteData?.upload || 0) }}
|
||||
<span class="text-base font-weight-regular" :class="getDiffClass(diffData?.upload)">
|
||||
@@ -377,7 +381,7 @@ onBeforeMount(async () => {
|
||||
<VCardText class="d-flex align-center">
|
||||
<div class="d-flex justify-space-between" style="inline-size: 100%">
|
||||
<div class="d-flex flex-column gap-y-1 overflow-hidden">
|
||||
<span class="text-base">总下载量</span>
|
||||
<span class="text-base">{{ t('dialog.siteUserData.downloadTotal') }}</span>
|
||||
<h5 class="text-h5 d-flex align-center gap-2 text-wrap">
|
||||
{{ formatFileSize(siteData?.download || 0) }}
|
||||
<span class="text-base font-weight-regular" :class="getDiffClass(diffData?.download)">
|
||||
@@ -398,7 +402,7 @@ onBeforeMount(async () => {
|
||||
<VCardText class="d-flex align-center">
|
||||
<div class="d-flex justify-space-between" style="inline-size: 100%">
|
||||
<div class="d-flex flex-column gap-y-1 overflow-hidden">
|
||||
<span class="text-base">总做种数</span>
|
||||
<span class="text-base">{{ t('dialog.siteUserData.seedingCount') }}</span>
|
||||
<h5 class="text-h5 d-flex align-center gap-2 text-wrap">
|
||||
{{ siteData?.seeding?.toLocaleString() }}
|
||||
<span class="text-base font-weight-regular" :class="getDiffClass(diffData?.seeding)">
|
||||
@@ -419,7 +423,7 @@ onBeforeMount(async () => {
|
||||
<VCardText class="d-flex align-center">
|
||||
<div class="d-flex justify-space-between" style="inline-size: 100%">
|
||||
<div class="d-flex flex-column gap-y-1 overflow-hidden">
|
||||
<span class="text-base">总做种体积</span>
|
||||
<span class="text-base">{{ t('dialog.siteUserData.seedingSize') }}</span>
|
||||
<h5 class="text-h5 d-flex align-center gap-2 text-wrap">
|
||||
{{ formatFileSize(siteData?.seeding_size || 0) }}
|
||||
<span class="text-base font-weight-regular" :class="getDiffClass(diffData?.seeding_size)">
|
||||
@@ -440,7 +444,7 @@ onBeforeMount(async () => {
|
||||
<VCardText class="d-flex align-center">
|
||||
<div class="d-flex justify-space-between" style="inline-size: 100%">
|
||||
<div class="d-flex flex-column gap-y-1 overflow-hidden">
|
||||
<span class="text-base">加入时间</span>
|
||||
<span class="text-base">{{ t('dialog.siteUserData.joinTime') }}</span>
|
||||
<h5 class="text-h5 d-flex align-center gap-2 text-wrap">
|
||||
{{ siteData?.join_at?.split(' ')[0] }}
|
||||
</h5>
|
||||
@@ -455,7 +459,7 @@ onBeforeMount(async () => {
|
||||
</VRow>
|
||||
<VRow>
|
||||
<VCol>
|
||||
<VCard title="历史流量">
|
||||
<VCard :title="t('dialog.siteUserData.trafficHistory')">
|
||||
<VCardText>
|
||||
<VApexChart type="line" :options="historyChartOptions" :series="historySeries" :height="300" />
|
||||
</VCardText>
|
||||
@@ -464,7 +468,7 @@ onBeforeMount(async () => {
|
||||
</VRow>
|
||||
<VRow>
|
||||
<VCol>
|
||||
<VCard title="做种分布">
|
||||
<VCard :title="t('dialog.siteUserData.seedingDistribution')">
|
||||
<VCardText>
|
||||
<VApexChart type="scatter" :options="seedingChartOptions" :series="seedingSeries" :height="300" />
|
||||
</VCardText>
|
||||
@@ -474,6 +478,6 @@ onBeforeMount(async () => {
|
||||
</VCardText>
|
||||
</VCard>
|
||||
<!-- 进度框 -->
|
||||
<ProgressDialog v-if="progressDialog" v-model="progressDialog" text="正在刷新站点数据..." />
|
||||
<ProgressDialog v-if="progressDialog" v-model="progressDialog" :text="t('dialog.siteUserData.refreshing')" />
|
||||
</VDialog>
|
||||
</template>
|
||||
|
||||
@@ -5,6 +5,10 @@ import api from '@/api'
|
||||
import type { DownloaderConf, FilterRuleGroup, Site, Subscribe, TransferDirectoryConf } from '@/api/types'
|
||||
import { useDisplay } from 'vuetify'
|
||||
import { useConfirm } from 'vuetify-use-dialog'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
// i18n
|
||||
const { t } = useI18n()
|
||||
|
||||
// 显示器宽度
|
||||
const display = useDisplay()
|
||||
@@ -80,7 +84,7 @@ const episodeGroupOptions = computed(() => {
|
||||
// 生成1到100季的下拉框选项
|
||||
const seasonItems = ref(
|
||||
Array.from({ length: 101 }, (_, i) => i).map(item => ({
|
||||
title: `第 ${item} 季`,
|
||||
title: t('dialog.subscribeEdit.seasonFormat', { number: item }),
|
||||
value: item,
|
||||
})),
|
||||
)
|
||||
@@ -106,7 +110,7 @@ async function loadDownloaderSetting() {
|
||||
try {
|
||||
const downloaders: DownloaderConf[] = await api.get('download/clients')
|
||||
downloaderOptions.value = [
|
||||
{ title: '默认', value: '' },
|
||||
{ title: t('common.default'), value: '' },
|
||||
...downloaders.map((item: { name: any }) => ({
|
||||
title: item.name,
|
||||
value: item.name,
|
||||
@@ -229,8 +233,8 @@ async function getSubscribeInfo() {
|
||||
// 删除订阅
|
||||
async function removeSubscribe() {
|
||||
const isConfirmed = await createConfirm({
|
||||
title: '确认',
|
||||
content: `是否确认取消订阅?`,
|
||||
title: t('common.confirm'),
|
||||
content: t('dialog.subscribeEdit.cancelSubscribeConfirm'),
|
||||
})
|
||||
|
||||
if (!isConfirmed) return
|
||||
@@ -268,7 +272,7 @@ const targetDirectories = computed(() => {
|
||||
// 质量选择框数据
|
||||
const qualityOptions = ref([
|
||||
{
|
||||
title: '全部',
|
||||
title: t('common.all'),
|
||||
value: '',
|
||||
},
|
||||
{
|
||||
@@ -308,7 +312,7 @@ const qualityOptions = ref([
|
||||
// 分辨率选择框数据
|
||||
const resolutionOptions = ref([
|
||||
{
|
||||
title: '全部',
|
||||
title: t('common.all'),
|
||||
value: '',
|
||||
},
|
||||
{
|
||||
@@ -328,7 +332,7 @@ const resolutionOptions = ref([
|
||||
// 特效选择框数据
|
||||
const effectOptions = ref([
|
||||
{
|
||||
title: '全部',
|
||||
title: t('common.all'),
|
||||
value: '',
|
||||
},
|
||||
{
|
||||
@@ -362,11 +366,16 @@ onMounted(() => {
|
||||
<template>
|
||||
<VDialog scrollable max-width="45rem" :fullscreen="!display.mdAndUp.value">
|
||||
<VCard
|
||||
:title="`${
|
||||
:title="
|
||||
props.default
|
||||
? `${props.type}默认订阅规则`
|
||||
: `编辑订阅 - ${subscribeForm.name} ${subscribeForm.season ? `第 ${subscribeForm.season} 季` : ''}`
|
||||
}`"
|
||||
? t('dialog.subscribeEdit.titleDefault')
|
||||
: t('dialog.subscribeEdit.titleEditFormat', {
|
||||
name: subscribeForm.name,
|
||||
season: subscribeForm.season
|
||||
? t('dialog.subscribeEdit.seasonFormat', { number: subscribeForm.season })
|
||||
: '',
|
||||
})
|
||||
"
|
||||
class="rounded-t"
|
||||
>
|
||||
<VCardText>
|
||||
@@ -374,10 +383,10 @@ onMounted(() => {
|
||||
<VForm @submit.prevent="() => {}">
|
||||
<VTabs v-model="activeTab" show-arrows>
|
||||
<VTab value="basic">
|
||||
<div>基础</div>
|
||||
<div>{{ t('dialog.subscribeEdit.tabs.basic') }}</div>
|
||||
</VTab>
|
||||
<VTab value="advance">
|
||||
<div>进阶</div>
|
||||
<div>{{ t('dialog.subscribeEdit.tabs.advance') }}</div>
|
||||
</VTab>
|
||||
</VTabs>
|
||||
<VWindow v-model="activeTab" class="mt-5 disable-tab-transition" :touch="false">
|
||||
@@ -387,26 +396,26 @@ onMounted(() => {
|
||||
<VCol cols="12" md="4">
|
||||
<VTextField
|
||||
v-model="subscribeForm.keyword"
|
||||
label="搜索关键词"
|
||||
hint="指定搜索站点时使用的关键词"
|
||||
:label="t('dialog.subscribeEdit.searchKeyword')"
|
||||
:hint="t('dialog.subscribeEdit.searchKeywordHint')"
|
||||
persistent-hint
|
||||
/>
|
||||
</VCol>
|
||||
<VCol v-if="subscribeForm.type === '电视剧'" cols="12" md="4">
|
||||
<VTextField
|
||||
v-model="subscribeForm.total_episode"
|
||||
label="总集数"
|
||||
:label="t('dialog.subscribeEdit.totalEpisode')"
|
||||
:rules="[numberValidator]"
|
||||
hint="剧集总集数"
|
||||
:hint="t('dialog.subscribeEdit.totalEpisodeHint')"
|
||||
persistent-hint
|
||||
/>
|
||||
</VCol>
|
||||
<VCol v-if="subscribeForm.type === '电视剧'" cols="12" md="4">
|
||||
<VTextField
|
||||
v-model="subscribeForm.start_episode"
|
||||
label="开始集数"
|
||||
:label="t('dialog.subscribeEdit.startEpisode')"
|
||||
:rules="[numberValidator]"
|
||||
hint="开始订阅集数"
|
||||
:hint="t('dialog.subscribeEdit.startEpisodeHint')"
|
||||
persistent-hint
|
||||
/>
|
||||
</VCol>
|
||||
@@ -415,27 +424,27 @@ onMounted(() => {
|
||||
<VCol cols="12" md="4">
|
||||
<VSelect
|
||||
v-model="subscribeForm.quality"
|
||||
label="质量"
|
||||
:label="t('dialog.subscribeEdit.quality')"
|
||||
:items="qualityOptions"
|
||||
hint="订阅资源质量"
|
||||
:hint="t('dialog.subscribeEdit.qualityHint')"
|
||||
persistent-hint
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="4">
|
||||
<VSelect
|
||||
v-model="subscribeForm.resolution"
|
||||
label="分辨率"
|
||||
:label="t('dialog.subscribeEdit.resolution')"
|
||||
:items="resolutionOptions"
|
||||
hint="订阅资源分辨率"
|
||||
:hint="t('dialog.subscribeEdit.resolutionHint')"
|
||||
persistent-hint
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="4">
|
||||
<VSelect
|
||||
v-model="subscribeForm.effect"
|
||||
label="特效"
|
||||
:label="t('dialog.subscribeEdit.effect')"
|
||||
:items="effectOptions"
|
||||
hint="订阅资源特效"
|
||||
:hint="t('dialog.subscribeEdit.effectHint')"
|
||||
persistent-hint
|
||||
/>
|
||||
</VCol>
|
||||
@@ -446,10 +455,10 @@ onMounted(() => {
|
||||
v-model="subscribeForm.sites"
|
||||
:items="selectSitesOptions"
|
||||
chips
|
||||
label="订阅站点"
|
||||
:label="t('dialog.subscribeEdit.subscribeSites')"
|
||||
multiple
|
||||
clearable
|
||||
hint="订阅的站点范围,不选使用系统设置"
|
||||
:hint="t('dialog.subscribeEdit.subscribeSitesHint')"
|
||||
persistent-hint
|
||||
/>
|
||||
</VCol>
|
||||
@@ -459,8 +468,8 @@ onMounted(() => {
|
||||
<VSelect
|
||||
v-model="subscribeForm.downloader"
|
||||
:items="downloaderOptions"
|
||||
label="下载器"
|
||||
hint="指定该订阅使用的下载器"
|
||||
:label="t('dialog.subscribeEdit.downloader')"
|
||||
:hint="t('dialog.subscribeEdit.downloaderHint')"
|
||||
persistent-hint
|
||||
/>
|
||||
</VCol>
|
||||
@@ -468,8 +477,8 @@ onMounted(() => {
|
||||
<VCombobox
|
||||
v-model="subscribeForm.save_path"
|
||||
:items="targetDirectories"
|
||||
label="保存路径"
|
||||
hint="指定该订阅的下载保存路径,留空自动使用设定的下载目录"
|
||||
:label="t('dialog.subscribeEdit.savePath')"
|
||||
:hint="t('dialog.subscribeEdit.savePathHint')"
|
||||
persistent-hint
|
||||
/>
|
||||
</VCol>
|
||||
@@ -478,24 +487,24 @@ onMounted(() => {
|
||||
<VCol cols="12" md="4">
|
||||
<VSwitch
|
||||
v-model="subscribeForm.best_version"
|
||||
label="洗版"
|
||||
hint="根据洗版优先级进行洗版订阅"
|
||||
:label="t('dialog.subscribeEdit.bestVersion')"
|
||||
:hint="t('dialog.subscribeEdit.bestVersionHint')"
|
||||
persistent-hint
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="4">
|
||||
<VSwitch
|
||||
v-model="subscribeForm.search_imdbid"
|
||||
label="使用 ImdbID 搜索"
|
||||
hint="开使用 ImdbID 精确搜索资源"
|
||||
:label="t('dialog.subscribeEdit.searchImdbid')"
|
||||
:hint="t('dialog.subscribeEdit.searchImdbidHint')"
|
||||
persistent-hint
|
||||
/>
|
||||
</VCol>
|
||||
<VCol v-if="props.default" cols="12" md="4">
|
||||
<VSwitch
|
||||
v-model="subscribeForm.show_edit_dialog"
|
||||
label="订阅时编辑更多规则"
|
||||
hint="添加订阅时显示此编辑订阅对话框"
|
||||
:label="t('dialog.subscribeEdit.showEditDialog')"
|
||||
:hint="t('dialog.subscribeEdit.showEditDialogHint')"
|
||||
persistent-hint
|
||||
/>
|
||||
</VCol>
|
||||
@@ -508,16 +517,16 @@ onMounted(() => {
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField
|
||||
v-model="subscribeForm.include"
|
||||
label="包含(关键字、正则式)"
|
||||
hint="包含规则,支持正则表达式"
|
||||
:label="t('dialog.subscribeEdit.include')"
|
||||
:hint="t('dialog.subscribeEdit.includeHint')"
|
||||
persistent-hint
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField
|
||||
v-model="subscribeForm.exclude"
|
||||
label="排除(关键字、正则式)"
|
||||
hint="排除规则,支持正则表达式"
|
||||
:label="t('dialog.subscribeEdit.exclude')"
|
||||
:hint="t('dialog.subscribeEdit.excludeHint')"
|
||||
persistent-hint
|
||||
/>
|
||||
</VCol>
|
||||
@@ -530,8 +539,8 @@ onMounted(() => {
|
||||
chips
|
||||
multiple
|
||||
clearable
|
||||
label="优先级规则组"
|
||||
hint="按选定的过滤规则组对订阅进行过滤"
|
||||
:label="t('dialog.subscribeEdit.filterGroups')"
|
||||
:hint="t('dialog.subscribeEdit.filterGroupsHint')"
|
||||
persistent-hint
|
||||
/>
|
||||
</VCol>
|
||||
@@ -540,8 +549,8 @@ onMounted(() => {
|
||||
v-model="subscribeForm.episode_group"
|
||||
:items="episodeGroupOptions"
|
||||
:item-props="episodeGroupItemProps"
|
||||
label="指定剧集组"
|
||||
hint="按特定剧集组识别和刮削"
|
||||
:label="t('dialog.subscribeEdit.episodeGroup')"
|
||||
:hint="t('dialog.subscribeEdit.episodeGroupHint')"
|
||||
persistent-hint
|
||||
/>
|
||||
</VCol>
|
||||
@@ -549,16 +558,16 @@ onMounted(() => {
|
||||
<VSelect
|
||||
v-model="subscribeForm.season"
|
||||
:items="seasonItems"
|
||||
label="指定季"
|
||||
hint="指定任意季订阅"
|
||||
:label="t('dialog.subscribeEdit.season')"
|
||||
:hint="t('dialog.subscribeEdit.seasonHint')"
|
||||
persistent-hint
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" v-if="!props.default">
|
||||
<VTextField
|
||||
v-model="subscribeForm.media_category"
|
||||
label="自定义类别"
|
||||
hint="指定类别名称,留空自动识别"
|
||||
:label="t('dialog.subscribeEdit.mediaCategory')"
|
||||
:hint="t('dialog.subscribeEdit.mediaCategoryHint')"
|
||||
persistent-hint
|
||||
/>
|
||||
</VCol>
|
||||
@@ -567,14 +576,10 @@ onMounted(() => {
|
||||
<VCol cols="12">
|
||||
<VTextarea
|
||||
v-model="subscribeForm.custom_words"
|
||||
label="自定义识别词"
|
||||
hint="只对该订阅使用的识别词"
|
||||
:label="t('dialog.subscribeEdit.customWords')"
|
||||
:hint="t('dialog.subscribeEdit.customWordsHint')"
|
||||
persistent-hint
|
||||
placeholder="屏蔽词
|
||||
被替换词 => 替换词
|
||||
前定位词 <> 后定位词 >> 集偏移量(EP)
|
||||
被替换词 => 替换词 && 前定位词 <> 后定位词 >> 集偏移量(EP)
|
||||
其中替换词支持格式:{[tmdbid/doubanid=xxx;type=movie/tv;s=xxx;e=xxx]} 直接指定TMDBID/豆瓣ID识别,其中s、e为季数和集数(可选)"
|
||||
:placeholder="t('dialog.subscribeEdit.customWordsPlaceholder')"
|
||||
/>
|
||||
</VCol>
|
||||
</VRow>
|
||||
@@ -585,7 +590,7 @@ onMounted(() => {
|
||||
</VCardText>
|
||||
<VCardActions class="pt-3">
|
||||
<VBtn v-if="!props.default" color="error" @click="removeSubscribe" variant="outlined" class="me-3">
|
||||
取消订阅
|
||||
{{ t('dialog.subscribeEdit.cancelSubscribe') }}
|
||||
</VBtn>
|
||||
<VSpacer />
|
||||
<VBtn
|
||||
@@ -594,7 +599,7 @@ onMounted(() => {
|
||||
prepend-icon="mdi-content-save"
|
||||
class="px-5"
|
||||
>
|
||||
保存
|
||||
{{ t('dialog.subscribeEdit.save') }}
|
||||
</VBtn>
|
||||
</VCardActions>
|
||||
</VCard>
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
import api from '@/api'
|
||||
import { SubscrbieInfo } from '@/api/types'
|
||||
import { useDisplay } from 'vuetify'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
// i18n
|
||||
const { t } = useI18n()
|
||||
|
||||
// 显示器宽度
|
||||
const display = useDisplay()
|
||||
@@ -21,15 +25,15 @@ const subScribeInfo = ref<SubscrbieInfo>()
|
||||
|
||||
// 下载文件表头
|
||||
const downloadHeaders = [
|
||||
{ title: '集', key: 'episode_number', sortable: true },
|
||||
{ title: '种子', key: 'torrent_title', sortable: true },
|
||||
{ title: '文件', key: 'file_path', sortable: true },
|
||||
{ title: t('dialog.subscribeFiles.episodeColumn'), key: 'episode_number', sortable: true },
|
||||
{ title: t('dialog.subscribeFiles.torrentColumn'), key: 'torrent_title', sortable: true },
|
||||
{ title: t('dialog.subscribeFiles.fileColumn'), key: 'file_path', sortable: true },
|
||||
]
|
||||
|
||||
// 媒体库文件表头
|
||||
const libraryHeaders = [
|
||||
{ title: '集', key: 'episode_number', sortable: true },
|
||||
{ title: '文件', key: 'file_path', sortable: true },
|
||||
{ title: t('dialog.subscribeFiles.episodeColumn'), key: 'episode_number', sortable: true },
|
||||
{ title: t('dialog.subscribeFiles.fileColumn'), key: 'file_path', sortable: true },
|
||||
]
|
||||
|
||||
// 调用API查询订阅文件信息
|
||||
@@ -102,7 +106,7 @@ onBeforeMount(() => {
|
||||
{{ subScribeInfo?.subscribe?.name }}
|
||||
</div>
|
||||
<div v-if="subScribeInfo?.subscribe?.season" class="text-lg align-self-center align-self-lg-end ms-3">
|
||||
第 {{ subScribeInfo?.subscribe?.season }} 季
|
||||
{{ t('dialog.subscribeFiles.season', { number: subScribeInfo?.subscribe?.season }) }}
|
||||
</div>
|
||||
</h1>
|
||||
<div>{{ subScribeInfo?.subscribe?.year }}</div>
|
||||
@@ -119,13 +123,13 @@ onBeforeMount(() => {
|
||||
<VTab value="download" selected-class="v-slide-group-item--active v-tab--selected">
|
||||
<div>
|
||||
<VIcon size="20" start icon="mdi-download" />
|
||||
下载文件
|
||||
{{ t('dialog.subscribeFiles.downloadTab') }}
|
||||
</div>
|
||||
</VTab>
|
||||
<VTab value="library" selected-class="v-slide-group-item--active v-tab--selected">
|
||||
<div>
|
||||
<VIcon size="20" start icon="mdi-filmstrip-box-multiple" />
|
||||
媒体库文件
|
||||
{{ t('dialog.subscribeFiles.libraryTab') }}
|
||||
</div>
|
||||
</VTab>
|
||||
</VTabs>
|
||||
@@ -143,9 +147,9 @@ onBeforeMount(() => {
|
||||
return-object
|
||||
fixed-header
|
||||
hover
|
||||
items-per-page-text="每页条数"
|
||||
page-text="{0}-{1} 共 {2} 条"
|
||||
loading-text="加载中..."
|
||||
:items-per-page-text="t('dialog.subscribeFiles.itemsPerPage')"
|
||||
:page-text="t('dialog.subscribeFiles.pageText')"
|
||||
:loading-text="t('dialog.subscribeFiles.loadingText')"
|
||||
>
|
||||
<template #item.episode_number="{ item }">
|
||||
<div class="text-high-emphasis pt-1">{{ item.episode_number }}. {{ item.title }}</div>
|
||||
@@ -158,7 +162,7 @@ onBeforeMount(() => {
|
||||
<template #item.file_path="{ item }">
|
||||
<div class="text-xs" v-for="file in item.download">{{ file.file_path }}</div>
|
||||
</template>
|
||||
<template #no-data> 没有数据 </template>
|
||||
<template #no-data> {{ t('dialog.subscribeFiles.noData') }} </template>
|
||||
</VDataTable>
|
||||
</div>
|
||||
</transition>
|
||||
@@ -176,9 +180,9 @@ onBeforeMount(() => {
|
||||
return-object
|
||||
fixed-header
|
||||
hover
|
||||
items-per-page-text="每页条数"
|
||||
page-text="{0}-{1} 共 {2} 条"
|
||||
loading-text="加载中..."
|
||||
:items-per-page-text="t('dialog.subscribeFiles.itemsPerPage')"
|
||||
:page-text="t('dialog.subscribeFiles.pageText')"
|
||||
:loading-text="t('dialog.subscribeFiles.loadingText')"
|
||||
>
|
||||
<template #item.episode_number="{ item }">
|
||||
<div class="text-high-emphasis pt-1">{{ item.episode_number }}. {{ item.title }}</div>
|
||||
@@ -186,7 +190,7 @@ onBeforeMount(() => {
|
||||
<template #item.file_path="{ item }">
|
||||
<div class="text-xs" v-for="file in item.library">{{ file.file_path }}</div>
|
||||
</template>
|
||||
<template #no-data> 没有数据 </template>
|
||||
<template #no-data> {{ t('dialog.subscribeFiles.noData') }} </template>
|
||||
</VDataTable>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
@@ -3,6 +3,10 @@ import { formatFileSize } from '@/@core/utils/formatters'
|
||||
import api from '@/api'
|
||||
import { FileItem, TransferQueue } from '@/api/types'
|
||||
import { useDisplay } from 'vuetify'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
// 多语言支持
|
||||
const { t } = useI18n()
|
||||
|
||||
// 显示器宽度
|
||||
const display = useDisplay()
|
||||
@@ -16,7 +20,7 @@ const dataList = ref<TransferQueue[]>([])
|
||||
const progressEventSource = ref<EventSource>()
|
||||
|
||||
// 整理进度文本
|
||||
const progressText = ref('请稍候 ...')
|
||||
const progressText = ref(t('dialog.transferQueue.processing'))
|
||||
|
||||
// 整理进度
|
||||
const progressValue = ref(0)
|
||||
@@ -29,10 +33,11 @@ const activeTab = ref('')
|
||||
|
||||
// 状态标签
|
||||
const stateDict: { [key: string]: string } = {
|
||||
'waiting': '等待中',
|
||||
'running': '正在整理',
|
||||
'completed': '完成',
|
||||
'failed': '失败',
|
||||
'waiting': t('dialog.transferQueue.waitingState'),
|
||||
'running': t('dialog.transferQueue.runningState'),
|
||||
'completed': t('dialog.transferQueue.finishedState'),
|
||||
'failed': t('dialog.transferQueue.failedState'),
|
||||
'cancelled': t('dialog.transferQueue.cancelledState'),
|
||||
}
|
||||
|
||||
// 获取状态颜色
|
||||
@@ -88,13 +93,13 @@ async function remove_queue_task(fileitem: FileItem) {
|
||||
|
||||
// 使用SSE监听加载进度
|
||||
function startLoadingProgress() {
|
||||
progressText.value = '请稍候 ...'
|
||||
progressText.value = t('dialog.transferQueue.processing')
|
||||
progressEventSource.value = new EventSource(`${import.meta.env.VITE_API_BASE_URL}system/progress/filetransfer`)
|
||||
progressEventSource.value.onmessage = event => {
|
||||
const progress = JSON.parse(event.data)
|
||||
if (progress) {
|
||||
if (!progress.enable) {
|
||||
progressText.value = '请稍候 ...'
|
||||
progressText.value = t('dialog.transferQueue.processing')
|
||||
progressValue.value = 0
|
||||
if (refreshFlag.value) {
|
||||
refreshFlag.value = false
|
||||
@@ -138,7 +143,7 @@ onUnmounted(() => {
|
||||
<VDialog scrollable max-width="50rem" :fullscreen="!display.mdAndUp.value">
|
||||
<VCard class="mx-auto" width="100%">
|
||||
<VCardItem>
|
||||
<VCardTitle>整理队列</VCardTitle>
|
||||
<VCardTitle>{{ t('dialog.transferQueue.title') }}</VCardTitle>
|
||||
</VCardItem>
|
||||
<VDialogCloseBtn @click="emit('close')" />
|
||||
<VDivider />
|
||||
@@ -151,7 +156,7 @@ onUnmounted(() => {
|
||||
<VCardItem v-if="dataList.length > 0 && progressValue > 0" class="text-center pt-2">
|
||||
<span class="text-sm">{{ progressText }}</span>
|
||||
</VCardItem>
|
||||
<VCardText v-if="dataList.length === 0" class="text-center"> 没有正在整理的任务 </VCardText>
|
||||
<VCardText v-if="dataList.length === 0" class="text-center"> {{ t('dialog.transferQueue.noTasks') }} </VCardText>
|
||||
<VCardText>
|
||||
<VTabs v-model="activeTab" show-arrows class="v-tabs-pill" stacked>
|
||||
<VTab
|
||||
@@ -169,7 +174,7 @@ onUnmounted(() => {
|
||||
<VListItem v-for="task in activeTasks">
|
||||
<VListItemTitle>{{ task.fileitem.name }}</VListItemTitle>
|
||||
<VListItemSubtitle>
|
||||
大小:{{ formatFileSize(task.fileitem.size || 0) }}
|
||||
{{ t('dialog.transferQueue.sizeTitle') }}:{{ formatFileSize(task.fileitem.size || 0) }}
|
||||
<VChip size="small" :color="getStateColor(task.state)" class="ms-2">
|
||||
{{ stateDict[task.state] }}
|
||||
</VChip>
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
import { isNullOrEmptyObject } from '@/@core/utils'
|
||||
import api from '@/api'
|
||||
import { useToast } from 'vue-toast-notification'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
// 多语言支持
|
||||
const { t } = useI18n()
|
||||
|
||||
// 定义事件
|
||||
const emit = defineEmits(['done', 'close'])
|
||||
@@ -89,17 +93,17 @@ async function handleDone() {
|
||||
// 认证处理
|
||||
async function checkUser() {
|
||||
if (!authForm.value.site) {
|
||||
$toast.error('请选择认证站点!')
|
||||
$toast.error(t('dialog.userAuth.selectSiteRequired'))
|
||||
return
|
||||
}
|
||||
if (!authSites.value[authForm.value.site]) {
|
||||
$toast.error('站点配置不存在!')
|
||||
$toast.error(t('dialog.userAuth.siteConfigNotExist'))
|
||||
return
|
||||
}
|
||||
if (formFields.value.length > 0) {
|
||||
for (const field of formFields.value) {
|
||||
if (!authForm.value.params[field.site.toUpperCase() + '_' + field.key.toUpperCase()]) {
|
||||
$toast.error(`请输入${field.name}!`)
|
||||
$toast.error(t('dialog.userAuth.fieldRequired', { name: field.name }))
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -108,13 +112,13 @@ async function checkUser() {
|
||||
try {
|
||||
const result: { [key: string]: any } = await api.post(`site/auth`, authForm.value)
|
||||
if (result.success) {
|
||||
$toast.success('用户认证成功,请重新登录!')
|
||||
$toast.success(t('dialog.userAuth.authSuccess'))
|
||||
// 1秒后刷新页面
|
||||
setTimeout(() => {
|
||||
emit('done')
|
||||
}, 1000)
|
||||
} else {
|
||||
$toast.error(`认证失败:${result.message}`)
|
||||
$toast.error(t('dialog.userAuth.authFailed', { message: result.message }))
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
@@ -130,7 +134,7 @@ onMounted(async () => {
|
||||
|
||||
<template>
|
||||
<VDialog width="40rem" max-height="85vh">
|
||||
<VCard title="用户认证" class="rounded-t">
|
||||
<VCard :title="t('dialog.userAuth.title')" class="rounded-t">
|
||||
<VDialogCloseBtn @click="emit('close')" />
|
||||
<VCardText>
|
||||
<VRow>
|
||||
@@ -140,7 +144,7 @@ onMounted(async () => {
|
||||
:items="dropdownItems"
|
||||
item-value="key"
|
||||
item-title="name"
|
||||
label="选择认证站点"
|
||||
:label="t('dialog.userAuth.selectSite')"
|
||||
item-props
|
||||
>
|
||||
</VSelect>
|
||||
@@ -169,7 +173,7 @@ onMounted(async () => {
|
||||
size="large"
|
||||
:disabled="loading"
|
||||
>
|
||||
开始认证
|
||||
{{ t('dialog.userAuth.authBtn') }}
|
||||
</VBtn>
|
||||
</VCardText>
|
||||
</VCard>
|
||||
|
||||
@@ -9,6 +9,10 @@ import api from '@/api'
|
||||
import WorkflowSidebar from '@/layouts/components/WorkflowSidebar.vue'
|
||||
import DropzoneBackground from '@/layouts/components/DropzoneBackground.vue'
|
||||
import ImportCodeDialog from '@/components/dialog/ImportCodeDialog.vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
// 多语言支持
|
||||
const { t } = useI18n()
|
||||
|
||||
const { onConnect, addEdges, nodes, edges, addNodes, screenToFlowCoordinate } = useVueFlow()
|
||||
|
||||
@@ -18,7 +22,7 @@ const { onDragOver, onDrop, onDragLeave, isDragOver } = useDragAndDrop()
|
||||
onConnect((connection: Connection) => {
|
||||
// 双重校验
|
||||
if (!isValidConnection(connection)) {
|
||||
$toast.warning('非法连接:不能连接自身或同类型端口!')
|
||||
$toast.warning(t('dialog.workflowActions.invalidConnection'))
|
||||
return
|
||||
}
|
||||
addEdges(connection)
|
||||
@@ -67,7 +71,7 @@ const loadComponent = async (componentName: string) => {
|
||||
if (component) {
|
||||
return ((await component()) as any).default
|
||||
}
|
||||
throw new Error(`组件 ${componentName} 未找到`)
|
||||
throw new Error(t('dialog.workflowActions.componentNotFound', { component: componentName }))
|
||||
}
|
||||
|
||||
// 将所有components中的组件加载到nodeTypes中
|
||||
@@ -132,7 +136,7 @@ function handleComponentClick(action: any) {
|
||||
addNodes(newNode)
|
||||
|
||||
// 显示提示
|
||||
$toast.success('已添加组件到画布')
|
||||
$toast.success(t('dialog.workflowActions.componentAdded'))
|
||||
}
|
||||
|
||||
// 调用API 编辑任务
|
||||
@@ -144,10 +148,10 @@ async function updateWorkflow() {
|
||||
try {
|
||||
const result: { [key: string]: string } = await api.put(`workflow/${workflowForm.value.id}`, workflowForm.value)
|
||||
if (result.success) {
|
||||
$toast.success(`保存任务流程成功!`)
|
||||
$toast.success(t('dialog.workflowActions.saveSuccess'))
|
||||
emit('save')
|
||||
} else {
|
||||
$toast.error(`保存任务流程失败:${result.message}`)
|
||||
$toast.error(t('dialog.workflowActions.saveFailed', { message: result.message }))
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
@@ -164,10 +168,10 @@ function saveCodeString(type: string, code: any) {
|
||||
edges.value = codeObject.flows || []
|
||||
}
|
||||
importCodeDialog.value = false
|
||||
$toast.success('导入成功!')
|
||||
$toast.success(t('dialog.workflowActions.importSuccess'))
|
||||
}
|
||||
} catch (error) {
|
||||
$toast.error('导入失败!')
|
||||
$toast.error(t('dialog.workflowActions.importFailed'))
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
@@ -176,7 +180,7 @@ function saveCodeString(type: string, code: any) {
|
||||
function shareWorkflow() {
|
||||
const codeString = JSON.stringify({ actions: nodes.value, flows: edges.value })
|
||||
navigator.clipboard.writeText(codeString)
|
||||
$toast.success('任务流程代码已复制到剪贴板!')
|
||||
$toast.success(t('dialog.workflowActions.codeCopied'))
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
@@ -202,7 +206,7 @@ const isMacOS = computed(() => {
|
||||
<VIcon size="large" color="white" icon="mdi-close" />
|
||||
</VBtn>
|
||||
</VToolbarItems>
|
||||
<VToolbarTitle> 编辑流程 - {{ workflow?.name }} </VToolbarTitle>
|
||||
<VToolbarTitle> {{ t('dialog.workflowActions.title') }} - {{ workflow?.name }} </VToolbarTitle>
|
||||
<VSpacer></VSpacer>
|
||||
<VToolbarItems>
|
||||
<VBtn icon variant="text" @click="importCodeDialog = true" class="ms-2">
|
||||
@@ -248,7 +252,7 @@ const isMacOS = computed(() => {
|
||||
<ImportCodeDialog
|
||||
v-if="importCodeDialog"
|
||||
v-model="importCodeDialog"
|
||||
title="导入任务流程"
|
||||
:title="t('dialog.workflowActions.importTitle')"
|
||||
dataType="workflow"
|
||||
@close="importCodeDialog = false"
|
||||
@save="saveCodeString"
|
||||
|
||||
@@ -5,6 +5,10 @@ import { doneNProgress, startNProgress } from '@/api/nprogress'
|
||||
import { requiredValidator } from '@/@validators'
|
||||
import api from '@/api'
|
||||
import { useDisplay } from 'vuetify'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
// 多语言支持
|
||||
const { t } = useI18n()
|
||||
|
||||
// 输入参数
|
||||
const props = defineProps({
|
||||
@@ -13,7 +17,9 @@ const props = defineProps({
|
||||
})
|
||||
|
||||
// 新增或修改字样
|
||||
const title = computed(() => (props.workflow ? '编辑' : '创建'))
|
||||
const title = computed(() =>
|
||||
props.workflow ? t('dialog.workflowAddEdit.editTitle') : t('dialog.workflowAddEdit.addTitle'),
|
||||
)
|
||||
|
||||
// 显示器宽度
|
||||
const display = useDisplay()
|
||||
@@ -38,17 +44,17 @@ const $toast = useToast()
|
||||
// 调用API 新增任务
|
||||
async function addWorkflow() {
|
||||
if (!workflowForm.value.name || !workflowForm.value.timer) {
|
||||
$toast.error('请填写完整信息!')
|
||||
$toast.error(t('dialog.workflowAddEdit.nameRequired'))
|
||||
return
|
||||
}
|
||||
startNProgress()
|
||||
try {
|
||||
const result: { [key: string]: string } = await api.post('workflow/', workflowForm.value)
|
||||
if (result.success) {
|
||||
$toast.success(`创建任务成功,请编辑流程!`)
|
||||
$toast.success(t('dialog.workflowAddEdit.addSuccess'))
|
||||
emit('save')
|
||||
} else {
|
||||
$toast.error(`创建任务失败:${result.message}`)
|
||||
$toast.error(t('dialog.workflowAddEdit.addFailed', { message: result.message }))
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
@@ -59,17 +65,17 @@ async function addWorkflow() {
|
||||
// 调用API 编辑任务
|
||||
async function editWorkflow() {
|
||||
if (!workflowForm.value.name || !workflowForm.value.timer) {
|
||||
$toast.error('请填写完整信息!')
|
||||
$toast.error(t('dialog.workflowAddEdit.nameRequired'))
|
||||
return
|
||||
}
|
||||
startNProgress()
|
||||
try {
|
||||
const result: { [key: string]: string } = await api.put(`workflow/${workflowForm.value.id}`, workflowForm.value)
|
||||
if (result.success) {
|
||||
$toast.success(`修改任务成功!`)
|
||||
$toast.success(t('dialog.workflowAddEdit.editSuccess'))
|
||||
emit('save')
|
||||
} else {
|
||||
$toast.error(`修改任务失败:${result.message}`)
|
||||
$toast.error(t('dialog.workflowAddEdit.editFailed', { message: result.message }))
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
@@ -80,7 +86,7 @@ async function editWorkflow() {
|
||||
|
||||
<template>
|
||||
<VDialog scrollable :close-on-back="false" eager max-width="30rem" :fullscreen="!display.mdAndUp.value">
|
||||
<VCard :title="`${title}任务`" class="rounded-t">
|
||||
<VCard :title="title" class="rounded-t">
|
||||
<VDialogCloseBtn @click="emit('close')" />
|
||||
<VDivider />
|
||||
<VCardText>
|
||||
@@ -89,24 +95,28 @@ async function editWorkflow() {
|
||||
<VCol cols="12">
|
||||
<VTextField
|
||||
v-model="workflowForm.name"
|
||||
label="别名"
|
||||
:label="t('dialog.workflowAddEdit.name')"
|
||||
:rules="[requiredValidator]"
|
||||
persistent-hint
|
||||
hint="任务名称"
|
||||
:hint="t('dialog.workflowAddEdit.namePlaceholder')"
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12">
|
||||
<VCronField
|
||||
v-model="workflowForm.timer"
|
||||
label="定时"
|
||||
:label="t('dialog.workflowAddEdit.schedule')"
|
||||
:rules="[requiredValidator]"
|
||||
placeholder="5位cron表达式"
|
||||
persistent-hint
|
||||
hint="任务执行周期"
|
||||
:hint="t('dialog.workflowAddEdit.cronExprDesc')"
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12">
|
||||
<VTextarea v-model="workflowForm.description" label="任务描述" />
|
||||
<VTextarea
|
||||
v-model="workflowForm.description"
|
||||
:label="t('dialog.workflowAddEdit.desc')"
|
||||
:placeholder="t('dialog.workflowAddEdit.descPlaceholder')"
|
||||
/>
|
||||
</VCol>
|
||||
</VRow>
|
||||
</VForm>
|
||||
@@ -122,10 +132,10 @@ async function editWorkflow() {
|
||||
prepend-icon="mdi-content-save"
|
||||
class="px-5"
|
||||
>
|
||||
保存
|
||||
{{ t('dialog.workflowAddEdit.confirm') }}
|
||||
</VBtn>
|
||||
<VBtn v-else block color="primary" variant="elevated" @click="addWorkflow" prepend-icon="mdi-plus" class="px-5">
|
||||
创建
|
||||
{{ t('dialog.workflowAddEdit.confirm') }}
|
||||
</VBtn>
|
||||
</VCardActions>
|
||||
</VCard>
|
||||
|
||||
@@ -15,6 +15,8 @@ export default {
|
||||
send: 'Send',
|
||||
noData: 'No Data',
|
||||
noContent: 'No Content Found',
|
||||
all: 'All',
|
||||
default: 'Default',
|
||||
},
|
||||
theme: {
|
||||
light: 'Light',
|
||||
@@ -835,5 +837,310 @@ export default {
|
||||
scanned: 'QR code scanned, please confirm login',
|
||||
complete: 'Complete',
|
||||
},
|
||||
aliyunAuth: {
|
||||
loginTitle: 'Aliyun Drive Login',
|
||||
scanQrCode: 'Please scan with Aliyun Drive App',
|
||||
scanned: 'QR code scanned',
|
||||
complete: 'Complete',
|
||||
},
|
||||
rcloneConfig: {
|
||||
title: 'RClone Configuration',
|
||||
filePath: 'Rclone Config File Path',
|
||||
fileContent: 'Rclone Config File Content',
|
||||
defaultContent:
|
||||
'# Please fill in the rclone configuration file content here \n# Please refer to https://rclone.org/docs/ \n# Storage node name must be: MP',
|
||||
complete: 'Complete',
|
||||
},
|
||||
alistConfig: {
|
||||
title: 'Alist Configuration',
|
||||
serverUrl: 'Alist Server URL',
|
||||
username: 'Username',
|
||||
password: 'Password',
|
||||
tokenUrl: 'Token URL',
|
||||
loginType: 'Login Type',
|
||||
loginTypeOptions: {
|
||||
guest: 'Guest',
|
||||
username: 'Username & Password',
|
||||
token: 'Token',
|
||||
},
|
||||
complete: 'Complete',
|
||||
},
|
||||
workflowAddEdit: {
|
||||
addTitle: 'Add Workflow',
|
||||
editTitle: 'Edit Workflow',
|
||||
name: 'Name',
|
||||
namePlaceholder: 'Workflow name',
|
||||
desc: 'Description',
|
||||
descPlaceholder: 'Workflow description',
|
||||
enabled: 'Enabled',
|
||||
schedule: 'Schedule',
|
||||
cronExpr: 'Cron Expression',
|
||||
cronExprDesc: 'Cron expression for workflow scheduling',
|
||||
nameRequired: 'Please fill in all required information!',
|
||||
addSuccess: 'Workflow created successfully. Please edit the process!',
|
||||
addFailed: 'Failed to create workflow: {message}',
|
||||
editSuccess: 'Workflow modified successfully!',
|
||||
editFailed: 'Failed to modify workflow: {message}',
|
||||
cancel: 'Cancel',
|
||||
confirm: 'Confirm',
|
||||
},
|
||||
workflowActions: {
|
||||
title: 'Edit Workflow',
|
||||
noActionsMessage: 'No actions in the workflow, please add actions',
|
||||
addAction: 'Add Action',
|
||||
editAction: 'Edit Action',
|
||||
deleteAction: 'Delete Action',
|
||||
moveUp: 'Move Up',
|
||||
moveDown: 'Move Down',
|
||||
nameLabel: 'Action Name',
|
||||
nameRequired: 'Action name cannot be empty',
|
||||
typeLabel: 'Action Type',
|
||||
typeRequired: 'Action type cannot be empty',
|
||||
paramsLabel: 'Action Parameters',
|
||||
outputLabel: 'Action Output',
|
||||
saveAction: 'Save Action',
|
||||
cancelAction: 'Cancel',
|
||||
confirmDeleteTitle: 'Confirm Delete Action',
|
||||
confirmDeleteMessage: 'Are you sure you want to delete this action? This operation cannot be undone.',
|
||||
yesDelete: 'Yes, Delete',
|
||||
noCancel: 'Cancel',
|
||||
invalidConnection: 'Invalid connection: Cannot connect to self or same type port!',
|
||||
componentNotFound: 'Component {component} not found',
|
||||
componentAdded: 'Component added to canvas',
|
||||
saveSuccess: 'Workflow saved successfully!',
|
||||
saveFailed: 'Failed to save workflow: {message}',
|
||||
importTitle: 'Import Workflow',
|
||||
importSuccess: 'Import successful!',
|
||||
importFailed: 'Import failed!',
|
||||
codeCopied: 'Workflow code copied to clipboard!',
|
||||
},
|
||||
siteCookieUpdate: {
|
||||
title: 'Update Site Cookie',
|
||||
checkHint: 'Checking login status, please wait...',
|
||||
confirmUpdateTitle: 'Confirm Update',
|
||||
confirmUpdateMessage: "Do you want to update this site's cookie with the local cookie?",
|
||||
processing: 'Processing...',
|
||||
success: 'Cookie updated successfully',
|
||||
failed: 'Cookie update failed',
|
||||
confirm: 'Confirm',
|
||||
cancel: 'Cancel',
|
||||
},
|
||||
siteAddEdit: {
|
||||
addTitle: 'Add Site',
|
||||
editTitle: 'Edit Site',
|
||||
nameLabel: 'Site Name',
|
||||
urlLabel: 'Site URL',
|
||||
iconLabel: 'Site Icon',
|
||||
uploadIcon: 'Upload Icon',
|
||||
cookie: 'Cookie',
|
||||
rssUrl: 'RSS URL',
|
||||
enableLabel: 'Enable',
|
||||
pubEnableLabel: 'Public Resources',
|
||||
priorityLabel: 'Priority',
|
||||
signInLabel: 'Sign In',
|
||||
proxies: 'Proxies',
|
||||
userInfo: 'User Info',
|
||||
cancel: 'Cancel',
|
||||
confirm: 'Save',
|
||||
},
|
||||
pluginConfig: {
|
||||
title: 'Plugin Configuration',
|
||||
save: 'Save',
|
||||
close: 'Close',
|
||||
},
|
||||
pluginData: {
|
||||
title: 'Plugin Data',
|
||||
save: 'Save',
|
||||
close: 'Close',
|
||||
},
|
||||
pluginMarketSetting: {
|
||||
title: 'Plugin Market Settings',
|
||||
repoUrl: 'Plugin Repository URL',
|
||||
close: 'Close',
|
||||
save: 'Save',
|
||||
},
|
||||
userAuth: {
|
||||
title: 'User Authentication',
|
||||
codeLabel: 'Authentication Code',
|
||||
codePlaceholder: 'Please enter the authentication code',
|
||||
authBtn: 'Authenticate',
|
||||
closeBtn: 'Close',
|
||||
selectSite: 'Select Authentication Site',
|
||||
selectSiteRequired: 'Please select an authentication site!',
|
||||
siteConfigNotExist: 'Site configuration does not exist!',
|
||||
fieldRequired: 'Please enter {name}!',
|
||||
authSuccess: 'User authentication successful, please login again!',
|
||||
authFailed: 'Authentication failed: {message}',
|
||||
},
|
||||
transferQueue: {
|
||||
title: 'Transfer Queue',
|
||||
name: 'Name',
|
||||
type: 'Type',
|
||||
state: 'State',
|
||||
progress: 'Progress',
|
||||
startTime: 'Start Time',
|
||||
speedTitle: 'Speed',
|
||||
pathTitle: 'Path',
|
||||
sizeTitle: 'Size',
|
||||
waitingState: 'Waiting',
|
||||
runningState: 'Organizing',
|
||||
finishedState: 'Completed',
|
||||
failedState: 'Failed',
|
||||
cancelledState: 'Cancelled',
|
||||
noTasks: 'No tasks being organized',
|
||||
processing: 'Please wait ...',
|
||||
stopAll: 'Stop All',
|
||||
startAll: 'Start All',
|
||||
refresh: 'Refresh',
|
||||
close: 'Close',
|
||||
},
|
||||
reorganize: {
|
||||
title: 'Reorganize',
|
||||
sourceTitle: 'Source Files',
|
||||
targetTitle: 'Target Files',
|
||||
processingTitle: 'Processing',
|
||||
confirmTitle: 'Confirm',
|
||||
selectFile: 'Select File',
|
||||
selectTarget: 'Select Target',
|
||||
selectMediaType: 'Select Media Type',
|
||||
movie: 'Movie',
|
||||
tv: 'TV Show',
|
||||
selectTmdbId: 'Select TMDB ID',
|
||||
selectMediaInfo: 'Select Media Info',
|
||||
selectTargetPath: 'Select Target Path',
|
||||
selectTargetDir: 'Select Target Directory',
|
||||
selectFileName: 'Select Filename',
|
||||
confirmMoving: 'Please confirm moving!',
|
||||
sourceLabel: 'Source:',
|
||||
targetLabel: 'Target Directory:',
|
||||
filenameLabel: 'Filename:',
|
||||
close: 'Close',
|
||||
next: 'Next',
|
||||
previous: 'Previous',
|
||||
confirm: 'Confirm',
|
||||
},
|
||||
forkSubscribe: {
|
||||
title: 'Fork Subscription',
|
||||
selectSubscriber: 'Select Target User',
|
||||
overwriteExisting: 'Overwrite Existing',
|
||||
overwriteExistingHint: 'Whether to overwrite if the target user already has this subscription',
|
||||
confirm: 'Confirm',
|
||||
cancel: 'Cancel',
|
||||
},
|
||||
subscribeEdit: {
|
||||
titleDefault: 'Default Subscription Rule',
|
||||
titleEditFormat: 'Edit Subscription - {name} {season}',
|
||||
seasonFormat: 'Season {number}',
|
||||
tabs: {
|
||||
basic: 'Basic',
|
||||
advance: 'Advanced',
|
||||
},
|
||||
searchKeyword: 'Search Keyword',
|
||||
searchKeywordHint: 'Keyword used when searching sites',
|
||||
totalEpisode: 'Total Episodes',
|
||||
totalEpisodeHint: 'Total number of episodes',
|
||||
startEpisode: 'Start Episode',
|
||||
startEpisodeHint: 'Episode number to start subscription',
|
||||
quality: 'Quality',
|
||||
qualityHint: 'Resource quality for subscription',
|
||||
resolution: 'Resolution',
|
||||
resolutionHint: 'Resource resolution for subscription',
|
||||
effect: 'Effect',
|
||||
effectHint: 'Resource effect for subscription',
|
||||
subscribeSites: 'Subscribe Sites',
|
||||
subscribeSitesHint: 'Sites to subscribe from, use system settings if none selected',
|
||||
downloader: 'Downloader',
|
||||
downloaderHint: 'Specify which downloader to use for this subscription',
|
||||
savePath: 'Save Path',
|
||||
savePathHint: 'Specify download path for this subscription, leave empty to use default directories',
|
||||
bestVersion: 'Version Upgrade',
|
||||
bestVersionHint: 'Enable version upgrade based on priority',
|
||||
searchImdbid: 'Use ImdbID Search',
|
||||
searchImdbidHint: 'Use ImdbID for precise resource searching',
|
||||
showEditDialog: 'Edit More Rules When Subscribing',
|
||||
showEditDialogHint: 'Show this edit dialog when adding subscription',
|
||||
include: 'Include (Keywords, Regex)',
|
||||
includeHint: 'Include rules, supports regex',
|
||||
exclude: 'Exclude (Keywords, Regex)',
|
||||
excludeHint: 'Exclude rules, supports regex',
|
||||
filterGroups: 'Priority Rule Groups',
|
||||
filterGroupsHint: 'Filter subscription based on selected rule groups',
|
||||
episodeGroup: 'Episode Group',
|
||||
episodeGroupHint: 'Recognize and scrape based on specific episode group',
|
||||
season: 'Season',
|
||||
seasonHint: 'Specify season for subscription',
|
||||
mediaCategory: 'Custom Category',
|
||||
mediaCategoryHint: 'Specify category name, auto-detect if empty',
|
||||
customWords: 'Custom Recognition Words',
|
||||
customWordsHint: 'Recognition words only for this subscription',
|
||||
customWordsPlaceholder:
|
||||
'Filter word\nReplaced word => Replacement\nFront position word <> Back position word >> Episode offset (EP)\nReplaced word => Replacement && Front position word <> Back position word >> Episode offset (EP)\nReplacement format: {[tmdbid/doubanid=xxx;type=movie/tv;s=xxx;e=xxx]} to specify TMDBID/Douban ID, where s and e are season and episode (optional)',
|
||||
cancelSubscribe: 'Cancel Subscription',
|
||||
cancelSubscribeConfirm: 'Are you sure you want to cancel this subscription?',
|
||||
save: 'Save',
|
||||
},
|
||||
subscribeFiles: {
|
||||
title: 'Downloaded Files',
|
||||
noFilesMessage: 'No files',
|
||||
close: 'Close',
|
||||
downloadTab: 'Downloaded Files',
|
||||
libraryTab: 'Media Library Files',
|
||||
episodeColumn: 'Episode',
|
||||
torrentColumn: 'Torrent',
|
||||
fileColumn: 'File',
|
||||
itemsPerPage: 'Items per page',
|
||||
pageText: '{0}-{1} of {2} items',
|
||||
loadingText: 'Loading...',
|
||||
noData: 'No data',
|
||||
season: 'Season {number}',
|
||||
},
|
||||
subscribeHistory: {
|
||||
title: 'Subscription History',
|
||||
name: 'Name',
|
||||
time: 'Time',
|
||||
type: 'Type',
|
||||
mediaName: 'Media Name',
|
||||
message: 'Message',
|
||||
torrents: 'Torrents',
|
||||
episodes: 'Episodes',
|
||||
noRecords: 'No records',
|
||||
close: 'Close',
|
||||
},
|
||||
subscribeSeason: {
|
||||
title: 'Select Season',
|
||||
seasonTitle: 'Season {number}',
|
||||
close: 'Close',
|
||||
confirm: 'Confirm',
|
||||
},
|
||||
siteUserData: {
|
||||
title: 'Site User Data',
|
||||
updateTime: 'Update Time',
|
||||
username: 'Username',
|
||||
uploadTitle: 'Upload',
|
||||
uploadTotal: 'Total Upload',
|
||||
downloadTitle: 'Download',
|
||||
downloadTotal: 'Total Download',
|
||||
seedingTitle: 'Seeding',
|
||||
seedingCount: 'Total Seeding Count',
|
||||
seedingSize: 'Total Seeding Size',
|
||||
userLevel: 'User Level',
|
||||
msgCount: 'Unread Messages',
|
||||
inviteCount: 'Invites',
|
||||
bonus: 'Bonus Points',
|
||||
ratio: 'Share Ratio',
|
||||
joinTime: 'Join Time',
|
||||
trafficHistory: 'Traffic History',
|
||||
seedingDistribution: 'Seeding Distribution',
|
||||
volumeTitle: 'Volume',
|
||||
countTitle: 'Count: ',
|
||||
noData: 'None',
|
||||
refreshing: 'Refreshing site data...',
|
||||
close: 'Close',
|
||||
},
|
||||
siteResource: {
|
||||
title: 'Site Resources',
|
||||
searchHint: 'Search Resources',
|
||||
close: 'Close',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@ export default {
|
||||
send: '发送',
|
||||
noData: '暂无数据',
|
||||
noContent: '没有找到相关内容',
|
||||
all: '全部',
|
||||
default: '默认',
|
||||
},
|
||||
theme: {
|
||||
light: '浅色',
|
||||
@@ -820,5 +822,309 @@ export default {
|
||||
scanned: '已扫码,请确认登录',
|
||||
complete: '完成',
|
||||
},
|
||||
aliyunAuth: {
|
||||
loginTitle: '阿里云盘登录',
|
||||
scanQrCode: '请用阿里云盘 App 扫码',
|
||||
scanned: '已扫码',
|
||||
complete: '完成',
|
||||
},
|
||||
rcloneConfig: {
|
||||
title: 'RClone配置',
|
||||
filePath: 'rclone配置文件路径',
|
||||
fileContent: 'rclone配置文件内容',
|
||||
defaultContent: '# 请在此处填写rclone配置文件内容 \n# 请参考 https://rclone.org/docs/ \n# 存储节点名必须为:MP',
|
||||
complete: '完成',
|
||||
},
|
||||
alistConfig: {
|
||||
title: 'Alist配置',
|
||||
serverUrl: 'Alist服务地址',
|
||||
username: '用户名',
|
||||
password: '密码',
|
||||
tokenUrl: '获取Token地址',
|
||||
loginType: '登录方式',
|
||||
loginTypeOptions: {
|
||||
guest: '访客',
|
||||
username: '用户名密码',
|
||||
token: 'Token',
|
||||
},
|
||||
complete: '完成',
|
||||
},
|
||||
workflowAddEdit: {
|
||||
addTitle: '添加工作流',
|
||||
editTitle: '编辑工作流',
|
||||
name: '名称',
|
||||
namePlaceholder: '工作流名称',
|
||||
desc: '描述',
|
||||
descPlaceholder: '工作流描述',
|
||||
enabled: '启用',
|
||||
schedule: '定时执行',
|
||||
cronExpr: 'Cron表达式',
|
||||
cronExprDesc: '工作流定时执行的cron表达式',
|
||||
nameRequired: '请填写完整信息!',
|
||||
addSuccess: '创建任务成功,请编辑流程!',
|
||||
addFailed: '创建任务失败:{message}',
|
||||
editSuccess: '修改任务成功!',
|
||||
editFailed: '修改任务失败:{message}',
|
||||
cancel: '取消',
|
||||
confirm: '确认',
|
||||
},
|
||||
workflowActions: {
|
||||
title: '编辑流程',
|
||||
noActionsMessage: '工作流没有动作,请添加动作',
|
||||
addAction: '添加动作',
|
||||
editAction: '编辑动作',
|
||||
deleteAction: '删除动作',
|
||||
moveUp: '上移',
|
||||
moveDown: '下移',
|
||||
nameLabel: '动作名称',
|
||||
nameRequired: '动作名称不能为空',
|
||||
typeLabel: '动作类型',
|
||||
typeRequired: '动作类型不能为空',
|
||||
paramsLabel: '动作参数',
|
||||
outputLabel: '动作输出',
|
||||
saveAction: '保存动作',
|
||||
cancelAction: '取消',
|
||||
confirmDeleteTitle: '确认删除动作',
|
||||
confirmDeleteMessage: '确定要删除此动作吗?此操作无法撤销。',
|
||||
yesDelete: '是的,删除',
|
||||
noCancel: '取消',
|
||||
invalidConnection: '非法连接:不能连接自身或同类型端口!',
|
||||
componentNotFound: '组件 {component} 未找到',
|
||||
componentAdded: '已添加组件到画布',
|
||||
saveSuccess: '保存任务流程成功!',
|
||||
saveFailed: '保存任务流程失败:{message}',
|
||||
importTitle: '导入任务流程',
|
||||
importSuccess: '导入成功!',
|
||||
importFailed: '导入失败!',
|
||||
codeCopied: '任务流程代码已复制到剪贴板!',
|
||||
},
|
||||
siteCookieUpdate: {
|
||||
title: '更新站点Cookie',
|
||||
checkHint: '正在检查登录状态,请稍候...',
|
||||
confirmUpdateTitle: '确认更新',
|
||||
confirmUpdateMessage: '是否要用本地Cookie更新该站点的Cookie?',
|
||||
processing: '处理中...',
|
||||
success: '更新Cookie成功',
|
||||
failed: '更新Cookie失败',
|
||||
confirm: '确认',
|
||||
cancel: '取消',
|
||||
},
|
||||
siteAddEdit: {
|
||||
addTitle: '添加站点',
|
||||
editTitle: '编辑站点',
|
||||
nameLabel: '站点名称',
|
||||
urlLabel: '站点URL',
|
||||
iconLabel: '站点图标',
|
||||
uploadIcon: '上传图标',
|
||||
cookie: 'Cookie',
|
||||
rssUrl: 'RSS链接',
|
||||
enableLabel: '启用',
|
||||
pubEnableLabel: '资源公开',
|
||||
priorityLabel: '优先级',
|
||||
signInLabel: '签到',
|
||||
proxies: '代理',
|
||||
userInfo: '用户信息',
|
||||
cancel: '取消',
|
||||
confirm: '保存',
|
||||
},
|
||||
pluginConfig: {
|
||||
title: '插件配置',
|
||||
save: '保存',
|
||||
close: '关闭',
|
||||
},
|
||||
pluginData: {
|
||||
title: '插件数据',
|
||||
save: '保存',
|
||||
close: '关闭',
|
||||
},
|
||||
pluginMarketSetting: {
|
||||
title: '插件市场设置',
|
||||
repoUrl: '插件仓库地址',
|
||||
close: '关闭',
|
||||
save: '保存',
|
||||
},
|
||||
userAuth: {
|
||||
title: '用户认证',
|
||||
codeLabel: '认证码',
|
||||
codePlaceholder: '请输入认证码',
|
||||
authBtn: '开始认证',
|
||||
closeBtn: '关闭',
|
||||
selectSite: '选择认证站点',
|
||||
selectSiteRequired: '请选择认证站点!',
|
||||
siteConfigNotExist: '站点配置不存在!',
|
||||
fieldRequired: '请输入{name}!',
|
||||
authSuccess: '用户认证成功,请重新登录!',
|
||||
authFailed: '认证失败:{message}',
|
||||
},
|
||||
transferQueue: {
|
||||
title: '整理队列',
|
||||
name: '名称',
|
||||
type: '类型',
|
||||
state: '状态',
|
||||
progress: '进度',
|
||||
startTime: '开始时间',
|
||||
speedTitle: '速度',
|
||||
pathTitle: '路径',
|
||||
sizeTitle: '大小',
|
||||
waitingState: '等待中',
|
||||
runningState: '正在整理',
|
||||
finishedState: '完成',
|
||||
failedState: '失败',
|
||||
cancelledState: '已取消',
|
||||
noTasks: '没有正在整理的任务',
|
||||
processing: '请稍候 ...',
|
||||
stopAll: '全部停止',
|
||||
startAll: '全部开始',
|
||||
refresh: '刷新',
|
||||
close: '关闭',
|
||||
},
|
||||
reorganize: {
|
||||
title: '整理',
|
||||
sourceTitle: '源文件',
|
||||
targetTitle: '目标文件',
|
||||
processingTitle: '处理中',
|
||||
confirmTitle: '确认',
|
||||
selectFile: '选择文件',
|
||||
selectTarget: '选择目标',
|
||||
selectMediaType: '选择媒体类型',
|
||||
movie: '电影',
|
||||
tv: '电视剧',
|
||||
selectTmdbId: '选择TMDB ID',
|
||||
selectMediaInfo: '选择媒体信息',
|
||||
selectTargetPath: '选择目标路径',
|
||||
selectTargetDir: '选择目标目录',
|
||||
selectFileName: '选择文件名',
|
||||
confirmMoving: '请确认移动!',
|
||||
sourceLabel: '源文件:',
|
||||
targetLabel: '目标目录:',
|
||||
filenameLabel: '文件名:',
|
||||
close: '关闭',
|
||||
next: '下一步',
|
||||
previous: '上一步',
|
||||
confirm: '确认',
|
||||
},
|
||||
forkSubscribe: {
|
||||
title: '复制订阅',
|
||||
selectSubscriber: '选择复制目标',
|
||||
overwriteExisting: '覆盖现有订阅',
|
||||
overwriteExistingHint: '目标用户已存在该订阅时,是否覆盖',
|
||||
confirm: '确认',
|
||||
cancel: '取消',
|
||||
},
|
||||
subscribeEdit: {
|
||||
titleDefault: '默认订阅规则',
|
||||
titleEditFormat: '编辑订阅 - {name} {season}',
|
||||
seasonFormat: '第 {number} 季',
|
||||
tabs: {
|
||||
basic: '基础',
|
||||
advance: '进阶',
|
||||
},
|
||||
searchKeyword: '搜索关键词',
|
||||
searchKeywordHint: '指定搜索站点时使用的关键词',
|
||||
totalEpisode: '总集数',
|
||||
totalEpisodeHint: '剧集总集数',
|
||||
startEpisode: '开始集数',
|
||||
startEpisodeHint: '开始订阅集数',
|
||||
quality: '质量',
|
||||
qualityHint: '订阅资源质量',
|
||||
resolution: '分辨率',
|
||||
resolutionHint: '订阅资源分辨率',
|
||||
effect: '特效',
|
||||
effectHint: '订阅资源特效',
|
||||
subscribeSites: '订阅站点',
|
||||
subscribeSitesHint: '订阅的站点范围,不选使用系统设置',
|
||||
downloader: '下载器',
|
||||
downloaderHint: '指定该订阅使用的下载器',
|
||||
savePath: '保存路径',
|
||||
savePathHint: '指定该订阅的下载保存路径,留空自动使用设定的下载目录',
|
||||
bestVersion: '洗版',
|
||||
bestVersionHint: '根据洗版优先级进行洗版订阅',
|
||||
searchImdbid: '使用 ImdbID 搜索',
|
||||
searchImdbidHint: '开使用 ImdbID 精确搜索资源',
|
||||
showEditDialog: '订阅时编辑更多规则',
|
||||
showEditDialogHint: '添加订阅时显示此编辑订阅对话框',
|
||||
include: '包含(关键字、正则式)',
|
||||
includeHint: '包含规则,支持正则表达式',
|
||||
exclude: '排除(关键字、正则式)',
|
||||
excludeHint: '排除规则,支持正则表达式',
|
||||
filterGroups: '优先级规则组',
|
||||
filterGroupsHint: '按选定的过滤规则组对订阅进行过滤',
|
||||
episodeGroup: '指定剧集组',
|
||||
episodeGroupHint: '按特定剧集组识别和刮削',
|
||||
season: '指定季',
|
||||
seasonHint: '指定任意季订阅',
|
||||
mediaCategory: '自定义类别',
|
||||
mediaCategoryHint: '指定类别名称,留空自动识别',
|
||||
customWords: '自定义识别词',
|
||||
customWordsHint: '只对该订阅使用的识别词',
|
||||
customWordsPlaceholder:
|
||||
'屏蔽词\n被替换词 => 替换词\n前定位词 <> 后定位词 >> 集偏移量(EP)\n被替换词 => 替换词 && 前定位词 <> 后定位词 >> 集偏移量(EP)\n其中替换词支持格式:{[tmdbid/doubanid=xxx;type=movie/tv;s=xxx;e=xxx]} 直接指定TMDBID/豆瓣ID识别,其中s、e为季数和集数(可选)',
|
||||
cancelSubscribe: '取消订阅',
|
||||
save: '保存',
|
||||
cancelSubscribeConfirm: '是否确认取消订阅?',
|
||||
},
|
||||
subscribeFiles: {
|
||||
title: '已下载文件',
|
||||
noFilesMessage: '暂无文件',
|
||||
close: '关闭',
|
||||
downloadTab: '下载文件',
|
||||
libraryTab: '媒体库文件',
|
||||
episodeColumn: '集',
|
||||
torrentColumn: '种子',
|
||||
fileColumn: '文件',
|
||||
itemsPerPage: '每页条数',
|
||||
pageText: '{0}-{1} 共 {2} 条',
|
||||
loadingText: '加载中...',
|
||||
noData: '没有数据',
|
||||
season: '第 {number} 季',
|
||||
},
|
||||
subscribeHistory: {
|
||||
title: '订阅历史',
|
||||
name: '名称',
|
||||
time: '时间',
|
||||
type: '类型',
|
||||
mediaName: '媒体名称',
|
||||
message: '消息',
|
||||
torrents: '种子',
|
||||
episodes: '集数',
|
||||
noRecords: '无记录',
|
||||
close: '关闭',
|
||||
},
|
||||
subscribeSeason: {
|
||||
title: '选择季',
|
||||
seasonTitle: '第 {number} 季',
|
||||
close: '关闭',
|
||||
confirm: '确认',
|
||||
},
|
||||
siteUserData: {
|
||||
title: '站点用户数据',
|
||||
updateTime: '更新时间',
|
||||
username: '用户名',
|
||||
uploadTitle: '上传量',
|
||||
uploadTotal: '总上传量',
|
||||
downloadTitle: '下载量',
|
||||
downloadTotal: '总下载量',
|
||||
seedingTitle: '做种数',
|
||||
seedingCount: '总做种数',
|
||||
seedingSize: '总做种体积',
|
||||
userLevel: '用户等级',
|
||||
msgCount: '未读消息',
|
||||
inviteCount: '邀请数',
|
||||
bonus: '积分',
|
||||
ratio: '分享率',
|
||||
joinTime: '加入时间',
|
||||
trafficHistory: '历史流量',
|
||||
seedingDistribution: '做种分布',
|
||||
volumeTitle: '体积',
|
||||
countTitle: '数量:',
|
||||
noData: '无',
|
||||
refreshing: '正在刷新站点数据...',
|
||||
close: '关闭',
|
||||
},
|
||||
siteResource: {
|
||||
title: '站点资源',
|
||||
searchHint: '搜索资源',
|
||||
close: '关闭',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user