Merge pull request #339 from madrays/v2

This commit is contained in:
jxxghp
2025-05-26 11:26:26 +08:00
committed by GitHub
4 changed files with 296 additions and 0 deletions

View File

@@ -73,6 +73,18 @@ const imageLoadError = ref(false)
// 更新日志弹窗
const releaseDialog = ref(false)
// 插件分身对话框
const pluginCloneDialog = ref(false)
// 插件分身表单
const cloneForm = ref({
suffix: '',
name: '',
description: '',
version: '',
icon: ''
})
// 监听动作标识如为true则打开详情
watch(
() => props.action,
@@ -241,6 +253,54 @@ function configDone() {
emit('save')
}
// 显示插件分身对话框
function showPluginClone() {
cloneForm.value = {
suffix: '',
name: t('plugin.cloneDefaultName', { name: props.plugin?.plugin_name }),
description: t('plugin.cloneDefaultDescription', { description: props.plugin?.plugin_desc }),
version: props.plugin?.plugin_version || '1.0',
icon: props.plugin?.plugin_icon || ''
}
pluginCloneDialog.value = true
}
// 执行插件分身
async function executePluginClone() {
if (!cloneForm.value.suffix.trim()) {
$toast.error(t('plugin.suffixRequired'))
return
}
try {
progressDialog.value = true
progressText.value = t('plugin.cloning', { name: props.plugin?.plugin_name })
const result: { [key: string]: any } = await api.post(`plugin/clone/${props.plugin?.id}`, {
suffix: cloneForm.value.suffix.trim(),
name: cloneForm.value.name.trim(),
description: cloneForm.value.description.trim(),
version: cloneForm.value.version.trim(),
icon: cloneForm.value.icon.trim()
})
progressDialog.value = false
if (result.success) {
$toast.success(t('plugin.cloneSuccess', { name: cloneForm.value.name }))
pluginCloneDialog.value = false
// 通知父组件刷新
emit('remove')
} else {
$toast.error(t('plugin.cloneFailed', { message: result.message }))
}
} catch (error) {
progressDialog.value = false
$toast.error(t('plugin.cloneFailedGeneral'))
console.error(error)
}
}
// 弹出菜单
const dropdownItems = ref([
{
@@ -261,6 +321,16 @@ const dropdownItems = ref([
click: showPluginConfig,
},
},
{
title: t('plugin.clone'),
value: 8,
show: true,
props: {
prependIcon: 'mdi-content-copy',
color: 'info',
click: showPluginClone,
},
},
{
title: t('plugin.update'),
value: 3,
@@ -470,6 +540,136 @@ watch(
</VCardItem>
</VCard>
</VDialog>
<!-- 插件分身对话框 -->
<VDialog v-if="pluginCloneDialog" v-model="pluginCloneDialog" width="600" :fullscreen="!display.mdAndUp.value">
<VCard>
<VCardTitle class="d-flex align-center pa-4">
<VIcon icon="mdi-content-copy" class="me-3" color="primary" />
<div>
<div class="text-h6">🎭 {{ t('plugin.cloneTitle') }}</div>
<div class="text-caption text-medium-emphasis">{{ t('plugin.cloneSubtitle', { name: props.plugin?.plugin_name }) }}</div>
</div>
</VCardTitle>
<VDialogCloseBtn @click="pluginCloneDialog = false" />
<VDivider />
<VCardText class="pa-4">
<!-- 功能说明 -->
<VAlert
type="info"
variant="tonal"
density="compact"
class="mb-4"
icon="mdi-information-outline"
>
<div class="text-body-2">
<strong>{{ t('plugin.cloneFeature') }}</strong>{{ t('plugin.cloneDescription') }}
</div>
</VAlert>
<VForm>
<VRow>
<VCol cols="12" md="6">
<VTextField
v-model="cloneForm.suffix"
:label="t('plugin.suffix') + ' *'"
:placeholder="t('plugin.suffixPlaceholder')"
:hint="t('plugin.suffixHint')"
persistent-hint
:rules="[
v => !!v || t('plugin.suffixRequired'),
v => /^[a-zA-Z0-9]+$/.test(v) || t('plugin.suffixFormatError'),
v => v.length <= 20 || t('plugin.suffixLengthError')
]"
required
prepend-inner-icon="mdi-tag"
/>
</VCol>
<VCol cols="12" md="6">
<VTextField
v-model="cloneForm.name"
:label="t('plugin.cloneName')"
:placeholder="t('plugin.cloneNamePlaceholder')"
:hint="t('plugin.cloneNameHint')"
persistent-hint
prepend-inner-icon="mdi-rename-box"
/>
</VCol>
<VCol cols="12">
<VTextarea
v-model="cloneForm.description"
:label="t('plugin.cloneDescriptionLabel')"
:placeholder="t('plugin.cloneDescriptionPlaceholder')"
:hint="t('plugin.cloneDescriptionHint')"
persistent-hint
rows="2"
prepend-inner-icon="mdi-text"
/>
</VCol>
<VCol cols="12" md="6">
<VTextField
v-model="cloneForm.version"
:label="t('plugin.cloneVersion')"
:placeholder="t('plugin.cloneVersionPlaceholder')"
:hint="t('plugin.cloneVersionHint')"
persistent-hint
prepend-inner-icon="mdi-numeric"
/>
</VCol>
<VCol cols="12" md="6">
<VTextField
v-model="cloneForm.icon"
:label="t('plugin.cloneIcon')"
:placeholder="t('plugin.cloneIconPlaceholder')"
:hint="t('plugin.cloneIconHint')"
persistent-hint
prepend-inner-icon="mdi-image"
/>
</VCol>
<!-- 重要提醒 -->
<VCol cols="12">
<VAlert
type="warning"
variant="tonal"
density="compact"
class="mt-2"
icon="mdi-alert-circle-outline"
>
<div class="text-body-2">
<strong>{{ t('common.notice') }}</strong>{{ t('plugin.cloneNotice') }}
</div>
</VAlert>
</VCol>
</VRow>
</VForm>
</VCardText>
<VDivider />
<VCardActions class="pa-4">
<VSpacer />
<VBtn
@click="pluginCloneDialog = false"
variant="outlined"
>
{{ t('common.cancel') }}
</VBtn>
<VBtn
color="primary"
@click="executePluginClone"
:disabled="!cloneForm.suffix.trim()"
>
<VIcon icon="mdi-content-copy" class="me-2" />
{{ t('plugin.createClone') }}
</VBtn>
</VCardActions>
</VCard>
</VDialog>
</div>
</template>

View File

@@ -39,6 +39,7 @@ export default {
unsubscribe: 'Unsubscribe',
media: 'Media',
unknown: 'Unknown',
notice: 'Notice',
},
mediaType: {
movie: 'Movie',
@@ -2013,6 +2014,37 @@ export default {
folderName: 'Folder Name',
cancel: 'Cancel',
create: 'Create',
clone: 'Plugin Clone',
cloneTitle: 'Create Plugin Clone',
cloneSubtitle: 'Create an independent clone instance for {name}',
cloneFeature: 'Plugin Clone Feature',
cloneDescription: 'Create an independent copy of the plugin with separate configuration and data, suitable for multi-account, testing environments, etc.',
suffix: 'Clone Suffix',
suffixPlaceholder: 'e.g.: Test, Backup, Site1',
suffixHint: 'Unique identifier to distinguish clones, only letters and numbers allowed',
suffixRequired: 'Clone suffix cannot be empty',
suffixFormatError: 'Only letters and numbers allowed',
suffixLengthError: 'Length cannot exceed 20 characters',
cloneName: 'Clone Name',
cloneNamePlaceholder: 'e.g.: Auto Backup Test Version',
cloneNameHint: 'Display name for the clone plugin (optional)',
cloneDefaultName: '{name} Clone',
cloneDescriptionLabel: 'Clone Description',
cloneDescriptionPlaceholder: 'Describe the purpose and features of this clone...',
cloneDescriptionHint: 'Detailed description of the clone plugin purpose (optional)',
cloneDefaultDescription: '{description} (Clone Version)',
cloneVersion: 'Version',
cloneVersionPlaceholder: 'e.g.: 1.0, 2.1.0',
cloneVersionHint: 'Custom version number for the clone plugin (optional)',
cloneIcon: 'Icon URL',
cloneIconPlaceholder: 'https://example.com/icon.png',
cloneIconHint: 'Custom icon for the clone plugin (optional)',
cloneNotice: 'Clone plugins are disabled by default after creation and need to be manually configured and enabled. The clone suffix cannot be modified once set.',
createClone: 'Create Clone',
cloning: 'Creating clone for {name}...',
cloneSuccess: 'Plugin clone {name} created successfully!',
cloneFailed: 'Plugin clone creation failed: {message}',
cloneFailedGeneral: 'Plugin clone creation failed',
},
profile: {
personalInfo: 'Personal Information',

View File

@@ -39,6 +39,7 @@ export default {
unsubscribe: '取消订阅',
media: '媒体',
unknown: '未知',
notice: '注意',
},
mediaType: {
movie: '电影',
@@ -1989,6 +1990,37 @@ export default {
folderName: '文件夹名称',
cancel: '取消',
create: '创建',
clone: '插件分身',
cloneTitle: '创建插件分身',
cloneSubtitle: '为 {name} 创建独立的分身实例',
cloneFeature: '插件分身功能',
cloneDescription: '创建插件的独立副本,拥有独立的配置和数据,适用于多账号、测试环境等场景',
suffix: '分身后缀',
suffixPlaceholder: '例如Test、Backup、Site1',
suffixHint: '用于区分分身的唯一标识,只能包含英文字母和数字',
suffixRequired: '分身后缀不能为空',
suffixFormatError: '只能包含英文字母和数字',
suffixLengthError: '长度不能超过20个字符',
cloneName: '分身名称',
cloneNamePlaceholder: '例如:自动备份 测试版',
cloneNameHint: '分身插件的显示名称(可选)',
cloneDefaultName: '{name} 分身',
cloneDescriptionLabel: '分身描述',
cloneDescriptionPlaceholder: '描述这个分身的用途和特点...',
cloneDescriptionHint: '详细描述分身插件的用途(可选)',
cloneDefaultDescription: '{description} (分身版本)',
cloneVersion: '版本号',
cloneVersionPlaceholder: '例如1.0、2.1.0',
cloneVersionHint: '自定义分身插件的版本号(可选)',
cloneIcon: '图标URL',
cloneIconPlaceholder: 'https://example.com/icon.png',
cloneIconHint: '自定义分身插件的图标(可选)',
cloneNotice: '分身插件创建后默认为禁用状态,需要手动配置启用。分身后缀一旦确定无法修改。',
createClone: '创建分身',
cloning: '正在创建 {name} 的分身...',
cloneSuccess: '插件分身 {name} 创建成功!',
cloneFailed: '插件分身创建失败:{message}',
cloneFailedGeneral: '插件分身创建失败',
},
profile: {
personalInfo: '个人信息',

View File

@@ -39,6 +39,7 @@ export default {
unsubscribe: '取消訂閱',
media: '媒體',
unknown: '未知',
notice: '注意',
},
mediaType: {
movie: '電影',
@@ -1990,6 +1991,37 @@ export default {
folderName: '文件夾名稱',
cancel: '取消',
create: '創建',
clone: '插件分身',
cloneTitle: '創建插件分身',
cloneSubtitle: '為 {name} 創建獨立的分身實例',
cloneFeature: '插件分身功能',
cloneDescription: '創建插件的獨立副本,擁有獨立的配置和數據,適用於多賬號、測試環境等場景',
suffix: '分身後綴',
suffixPlaceholder: '例如Test、Backup、Site1',
suffixHint: '用於區分分身的唯一標識,只能包含英文字母和數字',
suffixRequired: '分身後綴不能為空',
suffixFormatError: '只能包含英文字母和數字',
suffixLengthError: '長度不能超過20個字符',
cloneName: '分身名稱',
cloneNamePlaceholder: '例如:自動備份 測試版',
cloneNameHint: '分身插件的顯示名稱(可選)',
cloneDefaultName: '{name} 分身',
cloneDescriptionLabel: '分身描述',
cloneDescriptionPlaceholder: '描述這個分身的用途和特點...',
cloneDescriptionHint: '詳細描述分身插件的用途(可選)',
cloneDefaultDescription: '{description} (分身版本)',
cloneVersion: '版本號',
cloneVersionPlaceholder: '例如1.0、2.1.0',
cloneVersionHint: '自定義分身插件的版本號(可選)',
cloneIcon: '圖標URL',
cloneIconPlaceholder: 'https://example.com/icon.png',
cloneIconHint: '自定義分身插件的圖標(可選)',
cloneNotice: '分身插件創建後默認為禁用狀態,需要手動配置啟用。分身後綴一旦確定無法修改。',
createClone: '創建分身',
cloning: '正在創建 {name} 的分身...',
cloneSuccess: '插件分身 {name} 創建成功!',
cloneFailed: '插件分身創建失敗:{message}',
cloneFailedGeneral: '插件分身創建失敗',
},
profile: {
personalInfo: '個人信息',