mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-05-31 21:30:33 +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>
|
||||
|
||||
Reference in New Issue
Block a user