Feature: 支持在配置文件配置可用的插件组 (#2505)
* feat: 增加可用插件集合配置项 * remove: 旧版平台可用性配置 已经基于多配置文件实现。 * feat: 应用配置文件插件可用性配置 * perf: hoist if from if
This commit is contained in:
@@ -5,6 +5,7 @@ import ListConfigItem from './ListConfigItem.vue'
|
||||
import ProviderSelector from './ProviderSelector.vue'
|
||||
import PersonaSelector from './PersonaSelector.vue'
|
||||
import KnowledgeBaseSelector from './KnowledgeBaseSelector.vue'
|
||||
import PluginSetSelector from './PluginSetSelector.vue'
|
||||
import { useI18n } from '@/i18n/composables'
|
||||
|
||||
|
||||
@@ -240,7 +241,34 @@ function hasVisibleItemsAfter(items, currentIndex) {
|
||||
v-model="createSelectorModel(itemKey).value"
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="itemMeta?._special === 'select_plugin_set'">
|
||||
<PluginSetSelector
|
||||
v-model="createSelectorModel(itemKey).value"
|
||||
/>
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<!-- Plugin Set Selector 全宽显示区域 -->
|
||||
<v-row v-if="!itemMeta?.invisible && itemMeta?._special === 'select_plugin_set'" class="plugin-set-display-row">
|
||||
<v-col cols="12" class="plugin-set-display">
|
||||
<div v-if="createSelectorModel(itemKey).value && createSelectorModel(itemKey).value.length > 0" class="selected-plugins-full-width">
|
||||
<div class="plugins-header">
|
||||
<small class="text-grey">已选择的插件:</small>
|
||||
</div>
|
||||
<div class="d-flex flex-wrap ga-2 mt-2">
|
||||
<v-chip
|
||||
v-for="plugin in (createSelectorModel(itemKey).value || [])"
|
||||
:key="plugin"
|
||||
size="small"
|
||||
label
|
||||
color="primary"
|
||||
variant="outlined"
|
||||
>
|
||||
{{ plugin === '*' ? '所有插件' : plugin }}
|
||||
</v-chip>
|
||||
</div>
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</template>
|
||||
@@ -386,6 +414,26 @@ function hasVisibleItemsAfter(items, currentIndex) {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.plugin-set-display-row {
|
||||
margin: 16px;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.plugin-set-display {
|
||||
padding: 0 8px;
|
||||
}
|
||||
|
||||
.selected-plugins-full-width {
|
||||
background-color: rgba(var(--v-theme-primary), 0.05);
|
||||
border: 1px solid rgba(var(--v-theme-primary), 0.1);
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.plugins-header {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.nested-object {
|
||||
padding-left: 8px;
|
||||
|
||||
@@ -0,0 +1,226 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- 顶部操作区域 -->
|
||||
<div class="d-flex align-center justify-space-between mb-2">
|
||||
<div class="flex-grow-1">
|
||||
<span v-if="!modelValue || modelValue.length === 0" style="color: rgb(var(--v-theme-primaryText));">
|
||||
未启用任何插件
|
||||
</span>
|
||||
<span v-else-if="isAllPlugins" style="color: rgb(var(--v-theme-primaryText));">
|
||||
启用所有插件 (*)
|
||||
</span>
|
||||
<span v-else style="color: rgb(var(--v-theme-primaryText));">
|
||||
已选择 {{ modelValue.length }} 个插件
|
||||
</span>
|
||||
</div>
|
||||
<v-btn size="small" color="primary" variant="tonal" @click="openDialog">
|
||||
{{ buttonText }}
|
||||
</v-btn>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Plugin Set Selection Dialog -->
|
||||
<v-dialog v-model="dialog" max-width="700px">
|
||||
<v-card>
|
||||
<v-card-title class="text-h3 py-4" style="font-weight: normal;">
|
||||
选择插件集合
|
||||
</v-card-title>
|
||||
|
||||
<v-card-text class="pa-4">
|
||||
<v-progress-linear v-if="loading" indeterminate color="primary"></v-progress-linear>
|
||||
|
||||
<div v-if="!loading">
|
||||
<!-- 预设选项 -->
|
||||
<v-radio-group v-model="selectionMode" class="mb-4" hide-details>
|
||||
<v-radio
|
||||
value="all"
|
||||
label="启用所有插件"
|
||||
color="primary"
|
||||
></v-radio>
|
||||
<v-radio
|
||||
value="none"
|
||||
label="不启用任何插件"
|
||||
color="primary"
|
||||
></v-radio>
|
||||
<v-radio
|
||||
value="custom"
|
||||
label="自定义选择"
|
||||
color="primary"
|
||||
></v-radio>
|
||||
</v-radio-group>
|
||||
|
||||
<!-- 自定义选择时显示插件列表 -->
|
||||
<div v-if="selectionMode === 'custom'" style="max-height: 300px; overflow-y: auto;">
|
||||
<v-list v-if="pluginList.length > 0" density="compact">
|
||||
<v-list-item
|
||||
v-for="plugin in pluginList"
|
||||
:key="plugin.name"
|
||||
rounded="md"
|
||||
class="ma-1">
|
||||
<template v-slot:prepend>
|
||||
<v-checkbox
|
||||
v-model="selectedPlugins"
|
||||
:value="plugin.name"
|
||||
color="primary"
|
||||
hide-details
|
||||
></v-checkbox>
|
||||
</template>
|
||||
|
||||
<v-list-item-title>{{ plugin.name }}</v-list-item-title>
|
||||
<v-list-item-subtitle>
|
||||
{{ plugin.desc || '无描述' }}
|
||||
<v-chip v-if="!plugin.activated" size="x-small" color="grey" class="ml-1">
|
||||
未激活
|
||||
</v-chip>
|
||||
</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
|
||||
<div class="pl-8 pt-2">
|
||||
<small>*不显示系统插件和已经在插件页禁用的插件。</small>
|
||||
</div>
|
||||
</v-list>
|
||||
|
||||
<div v-else class="text-center py-8">
|
||||
<v-icon size="64" color="grey-lighten-1">mdi-puzzle-outline</v-icon>
|
||||
<p class="text-grey mt-4">暂无可用的插件</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</v-card-text>
|
||||
|
||||
<v-card-actions class="pa-4">
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn variant="text" @click="cancelSelection">取消</v-btn>
|
||||
<v-btn
|
||||
color="primary"
|
||||
@click="confirmSelection">
|
||||
确认选择
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import axios from 'axios'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
buttonText: {
|
||||
type: String,
|
||||
default: '选择插件集合...'
|
||||
},
|
||||
maxDisplayItems: {
|
||||
type: Number,
|
||||
default: 3
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
const dialog = ref(false)
|
||||
const pluginList = ref([])
|
||||
const loading = ref(false)
|
||||
const selectionMode = ref('custom') // 'all', 'none', 'custom'
|
||||
const selectedPlugins = ref([])
|
||||
|
||||
// 判断是否为"所有插件"模式
|
||||
const isAllPlugins = computed(() => {
|
||||
return props.modelValue && props.modelValue.length === 1 && props.modelValue[0] === '*'
|
||||
})
|
||||
|
||||
// 移除插件
|
||||
function removePlugin(pluginName) {
|
||||
if (props.modelValue && props.modelValue.length > 0) {
|
||||
const newValue = props.modelValue.filter(name => name !== pluginName)
|
||||
emit('update:modelValue', newValue)
|
||||
}
|
||||
}
|
||||
|
||||
// 监听 modelValue 变化,同步内部状态
|
||||
watch(() => props.modelValue, (newValue) => {
|
||||
if (!newValue || newValue.length === 0) {
|
||||
selectionMode.value = 'none'
|
||||
selectedPlugins.value = []
|
||||
} else if (newValue.length === 1 && newValue[0] === '*') {
|
||||
selectionMode.value = 'all'
|
||||
selectedPlugins.value = []
|
||||
} else {
|
||||
selectionMode.value = 'custom'
|
||||
selectedPlugins.value = [...newValue]
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
async function openDialog() {
|
||||
dialog.value = true
|
||||
await loadPlugins()
|
||||
}
|
||||
|
||||
async function loadPlugins() {
|
||||
loading.value = true
|
||||
try {
|
||||
const response = await axios.get('/api/plugin/get')
|
||||
if (response.data.status === 'ok') {
|
||||
// 只显示已激活且非系统的插件,并按名称排序
|
||||
pluginList.value = (response.data.data || [])
|
||||
.filter(plugin => plugin.activated && !plugin.reserved)
|
||||
.sort((a, b) => a.name.localeCompare(b.name))
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载插件列表失败:', error)
|
||||
pluginList.value = []
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function confirmSelection() {
|
||||
let newValue = []
|
||||
|
||||
switch (selectionMode.value) {
|
||||
case 'all':
|
||||
newValue = ['*']
|
||||
break
|
||||
case 'none':
|
||||
newValue = []
|
||||
break
|
||||
case 'custom':
|
||||
newValue = [...selectedPlugins.value]
|
||||
break
|
||||
}
|
||||
|
||||
emit('update:modelValue', newValue)
|
||||
dialog.value = false
|
||||
}
|
||||
|
||||
function cancelSelection() {
|
||||
// 恢复到原始状态
|
||||
const currentValue = props.modelValue || []
|
||||
if (currentValue.length === 0) {
|
||||
selectionMode.value = 'none'
|
||||
selectedPlugins.value = []
|
||||
} else if (currentValue.length === 1 && currentValue[0] === '*') {
|
||||
selectionMode.value = 'all'
|
||||
selectedPlugins.value = []
|
||||
} else {
|
||||
selectionMode.value = 'custom'
|
||||
selectedPlugins.value = [...currentValue]
|
||||
}
|
||||
|
||||
dialog.value = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.v-list-item {
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.v-list-item:hover {
|
||||
background-color: rgba(var(--v-theme-primary), 0.04);
|
||||
}
|
||||
</style>
|
||||
@@ -16,7 +16,6 @@
|
||||
"buttons": {
|
||||
"showSystemPlugins": "Show System Extensions",
|
||||
"hideSystemPlugins": "Hide System Extensions",
|
||||
"platformConfig": "Platform Command Config",
|
||||
"install": "Install",
|
||||
"uninstall": "Uninstall",
|
||||
"update": "Update",
|
||||
@@ -88,18 +87,6 @@
|
||||
"title": "Error Information",
|
||||
"checkConsole": "Please check console for details"
|
||||
},
|
||||
"platformConfig": {
|
||||
"title": "Platform Command Availability Configuration",
|
||||
"description": "Set the availability of each extension on different platforms, check to enable",
|
||||
"noAdapters": "No Platform Adapters Found",
|
||||
"noAdaptersDesc": "Please add and configure platform adapters in Platform Management first, then set extension platform availability",
|
||||
"goPlatforms": "Go to Platform Management",
|
||||
"selectAll": "Select All",
|
||||
"selectAllNormal": "Select All Normal Extensions",
|
||||
"selectAllSystem": "Select All System Extensions",
|
||||
"selectNone": "Select None",
|
||||
"toggleAll": "Toggle All"
|
||||
},
|
||||
"config": {
|
||||
"title": "Extension Configuration",
|
||||
"noConfig": "This extension has no configuration"
|
||||
@@ -137,8 +124,6 @@
|
||||
"installing": "Installing extension from file",
|
||||
"installingFromUrl": "Installing extension from URL...",
|
||||
"installFailed": "Extension installation failed:",
|
||||
"getPlatformConfigFailed": "Failed to get platform extension config:",
|
||||
"savePlatformConfigFailed": "Failed to save platform extension config:",
|
||||
"getMarketDataFailed": "Failed to get extension market data:",
|
||||
"hasUpdate": "New version available:",
|
||||
"confirmDelete": "Are you sure you want to delete this extension?",
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
"buttons": {
|
||||
"showSystemPlugins": "显示系统插件",
|
||||
"hideSystemPlugins": "隐藏系统插件",
|
||||
"platformConfig": "平台命令配置",
|
||||
"install": "安装",
|
||||
"uninstall": "卸载",
|
||||
"update": "更新",
|
||||
@@ -88,18 +87,6 @@
|
||||
"title": "错误信息",
|
||||
"checkConsole": "详情请检查控制台"
|
||||
},
|
||||
"platformConfig": {
|
||||
"title": "平台命令可用性配置",
|
||||
"description": "设置每个插件在不同平台上的可用性,勾选表示启用",
|
||||
"noAdapters": "未找到平台适配器",
|
||||
"noAdaptersDesc": "请先在 平台管理 中添加并配置平台适配器,然后再设置插件的平台可用性",
|
||||
"goPlatforms": "前往平台管理",
|
||||
"selectAll": "全选",
|
||||
"selectAllNormal": "全选普通插件",
|
||||
"selectAllSystem": "全选系统插件",
|
||||
"selectNone": "全不选",
|
||||
"toggleAll": "反选"
|
||||
},
|
||||
"config": {
|
||||
"title": "插件配置",
|
||||
"noConfig": "这个插件没有配置"
|
||||
@@ -137,8 +124,6 @@
|
||||
"installing": "正在从文件安装插件",
|
||||
"installingFromUrl": "正在从链接安装插件...",
|
||||
"installFailed": "安装插件失败:",
|
||||
"getPlatformConfigFailed": "获取平台插件配置失败:",
|
||||
"savePlatformConfigFailed": "保存平台插件配置失败:",
|
||||
"getMarketDataFailed": "获取插件市场数据失败:",
|
||||
"hasUpdate": "有新版本:",
|
||||
"confirmDelete": "确定要删除插件吗?",
|
||||
|
||||
@@ -45,14 +45,6 @@ const readmeDialog = reactive({
|
||||
pluginName: '',
|
||||
repoUrl: null
|
||||
});
|
||||
// 平台插件配置
|
||||
const platformEnableDialog = ref(false);
|
||||
const platformEnableData = reactive({
|
||||
platforms: [],
|
||||
plugins: [],
|
||||
platform_enable: {}
|
||||
});
|
||||
const loadingPlatformData = ref(false);
|
||||
|
||||
// 新增变量支持列表视图
|
||||
const isListView = ref(false);
|
||||
@@ -326,100 +318,7 @@ const viewReadme = (plugin) => {
|
||||
readmeDialog.show = true;
|
||||
};
|
||||
|
||||
// 获取插件平台可用性配置
|
||||
const getPlatformEnableConfig = async () => {
|
||||
loadingPlatformData.value = true;
|
||||
try {
|
||||
const res = await axios.get('/api/plugin/platform_enable/get');
|
||||
if (res.data.status === "error") {
|
||||
toast(res.data.message, "error");
|
||||
return;
|
||||
}
|
||||
|
||||
platformEnableData.platforms = res.data.data.platforms;
|
||||
platformEnableData.plugins = res.data.data.plugins;
|
||||
platformEnableData.platform_enable = res.data.data.platform_enable;
|
||||
|
||||
// 如果没有平台,给出提示但仍显示对话框
|
||||
if (platformEnableData.platforms.length === 0) {
|
||||
toast(tm('dialogs.platformConfig.noAdaptersDesc'), "warning");
|
||||
} else {
|
||||
// 确保每个平台都有一个配置对象
|
||||
platformEnableData.platforms.forEach(platform => {
|
||||
if (!platformEnableData.platform_enable[platform.name]) {
|
||||
platformEnableData.platform_enable[platform.name] = {};
|
||||
}
|
||||
|
||||
// 确保每个插件在每个平台都有一个配置项
|
||||
platformEnableData.plugins.forEach(plugin => {
|
||||
if (platformEnableData.platform_enable[platform.name][plugin.name] === undefined) {
|
||||
platformEnableData.platform_enable[platform.name][plugin.name] = true; // 默认启用
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
platformEnableDialog.value = true;
|
||||
} catch (err) {
|
||||
toast(tm('messages.getPlatformConfigFailed') + " " + err, "error");
|
||||
} finally {
|
||||
loadingPlatformData.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 保存插件平台可用性配置
|
||||
const savePlatformEnableConfig = async () => {
|
||||
loadingPlatformData.value = true;
|
||||
try {
|
||||
const res = await axios.post('/api/plugin/platform_enable/set', {
|
||||
platform_enable: platformEnableData.platform_enable
|
||||
});
|
||||
|
||||
if (res.data.status === "error") {
|
||||
toast(res.data.message, "error");
|
||||
return;
|
||||
}
|
||||
|
||||
toast(res.data.message, "success");
|
||||
platformEnableDialog.value = false;
|
||||
} catch (err) {
|
||||
toast(tm('messages.savePlatformConfigFailed') + " " + err, "error");
|
||||
} finally {
|
||||
loadingPlatformData.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 全选指定平台的所有插件
|
||||
const selectAllPluginsForPlatform = (platformName, isSelected, onlyReserved = null) => {
|
||||
// 确保平台存在于platform_enable中
|
||||
if (!platformEnableData.platform_enable[platformName]) {
|
||||
platformEnableData.platform_enable[platformName] = {};
|
||||
}
|
||||
|
||||
// 为所有插件设置相同的状态
|
||||
platformEnableData.plugins.forEach(plugin => {
|
||||
// 如果onlyReserved为null,处理所有插件
|
||||
// 如果onlyReserved为true,只处理系统插件
|
||||
// 如果onlyReserved为false,只处理非系统插件
|
||||
if (onlyReserved === null || plugin.reserved === onlyReserved) {
|
||||
platformEnableData.platform_enable[platformName][plugin.name] = isSelected;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 反选指定平台的所有插件
|
||||
const toggleAllPluginsForPlatform = (platformName) => {
|
||||
// 确保平台存在于platform_enable中
|
||||
if (!platformEnableData.platform_enable[platformName]) {
|
||||
platformEnableData.platform_enable[platformName] = {};
|
||||
}
|
||||
|
||||
// 对每个插件进行反选操作
|
||||
platformEnableData.plugins.forEach(plugin => {
|
||||
const currentState = platformEnableData.platform_enable[platformName][plugin.name];
|
||||
platformEnableData.platform_enable[platformName][plugin.name] = !currentState;
|
||||
});
|
||||
};
|
||||
|
||||
const open = (link) => {
|
||||
if (link) {
|
||||
@@ -685,11 +584,6 @@ onMounted(async () => {
|
||||
{{ showReserved ? tm('buttons.hideSystemPlugins') : tm('buttons.showSystemPlugins') }}
|
||||
</v-btn>
|
||||
|
||||
<v-btn class="ml-2" variant="tonal" @click="getPlatformEnableConfig">
|
||||
<v-icon>mdi-cog</v-icon>
|
||||
{{ tm('buttons.platformConfig') }}
|
||||
</v-btn>
|
||||
|
||||
<v-btn class="ml-2" color="primary" variant="tonal" @click="dialog = true">
|
||||
<v-icon>mdi-plus</v-icon>
|
||||
{{ tm('buttons.install') }}
|
||||
@@ -965,96 +859,6 @@ onMounted(async () => {
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<!-- 插件平台配置对话框 -->
|
||||
<v-dialog v-model="platformEnableDialog" max-width="900" persistent>
|
||||
<v-card class="rounded-lg">
|
||||
<v-toolbar color="primary" density="comfortable" flat>
|
||||
<v-toolbar-title class="text-white">{{ tm('dialogs.platformConfig.title') }}</v-toolbar-title>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn icon @click="platformEnableDialog = false" variant="text" color="white">
|
||||
<v-icon>mdi-close</v-icon>
|
||||
</v-btn>
|
||||
</v-toolbar>
|
||||
|
||||
<v-card-text class="pt-4">
|
||||
<p class="text-body-2 mb-4">{{ tm('dialogs.platformConfig.description') }}</p>
|
||||
|
||||
<v-overlay :model-value="loadingPlatformData" class="align-center justify-center" persistent>
|
||||
<v-progress-circular color="primary" indeterminate size="64"></v-progress-circular>
|
||||
</v-overlay>
|
||||
|
||||
<div v-if="platformEnableData.platforms.length === 0" class="text-center pa-8">
|
||||
<v-icon icon="mdi-alert" color="warning" size="64" class="mb-4"></v-icon>
|
||||
<div class="text-h5 mb-2">{{ tm('dialogs.platformConfig.noAdapters') }}</div>
|
||||
<div class="text-body-1 mb-4">{{ tm('dialogs.platformConfig.noAdaptersDesc') }}</div>
|
||||
<v-btn color="primary" to="/platforms" variant="elevated">{{ tm('dialogs.platformConfig.goPlatforms')
|
||||
}}</v-btn>
|
||||
</div>
|
||||
|
||||
<v-sheet v-else class="rounded-lg overflow-hidden">
|
||||
<v-table hover class="elevation-1">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-left">{{ tm('table.headers.name') }}</th>
|
||||
<th v-for="platform in platformEnableData.platforms" :key="platform.name">
|
||||
<div class="d-flex align-center">
|
||||
{{ platform.display_name }}
|
||||
<v-menu>
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-btn icon density="compact" variant="text" size="small" v-bind="props" class="ms-1">
|
||||
<v-icon>mdi-dots-vertical</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-list>
|
||||
<v-list-item @click="selectAllPluginsForPlatform(platform.name, true)">
|
||||
<v-list-item-title>{{ tm('dialogs.platformConfig.selectAll') }}</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item @click="selectAllPluginsForPlatform(platform.name, true, false)">
|
||||
<v-list-item-title>{{ tm('dialogs.platformConfig.selectAllNormal') }}</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item @click="selectAllPluginsForPlatform(platform.name, true, true)">
|
||||
<v-list-item-title>{{ tm('dialogs.platformConfig.selectAllSystem') }}</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item @click="selectAllPluginsForPlatform(platform.name, false)">
|
||||
<v-list-item-title>{{ tm('dialogs.platformConfig.selectNone') }}</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item @click="toggleAllPluginsForPlatform(platform.name)">
|
||||
<v-list-item-title>{{ tm('dialogs.platformConfig.toggleAll') }}</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="plugin in platformEnableData.plugins" :key="plugin.name">
|
||||
<td>
|
||||
<div class="d-flex align-center">
|
||||
{{ plugin.name }}
|
||||
<v-chip v-if="plugin.reserved" color="primary" size="x-small" class="ml-2">{{ tm('status.system')
|
||||
}}</v-chip>
|
||||
</div>
|
||||
<div class="text-caption text-grey">{{ plugin.desc }}</div>
|
||||
</td>
|
||||
<td v-for="platform in platformEnableData.platforms" :key="platform.name">
|
||||
<v-checkbox v-model="platformEnableData.platform_enable[platform.name][plugin.name]" hide-details
|
||||
density="compact"></v-checkbox>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</v-table>
|
||||
</v-sheet>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn color="grey" text @click="platformEnableDialog = false">{{ tm('buttons.close') }}</v-btn>
|
||||
<v-btn v-if="platformEnableData.platforms.length > 0" color="primary" @click="savePlatformEnableConfig">{{
|
||||
tm('buttons.save') }}</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
<!-- 配置对话框 -->
|
||||
<v-dialog v-model="configDialog" width="1000">
|
||||
<v-card>
|
||||
|
||||
Reference in New Issue
Block a user