feat(extensions): add ZIP installation and unify action dialogs
This commit is contained in:
@@ -1,4 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import ActionDialog from '@/components/common/ActionDialog.vue'
|
||||
import { useActionDialog } from '@/composables/useActionDialog'
|
||||
const { actionDialog, resolveAction, askConfirm } = useActionDialog()
|
||||
import { Key, Refresh } from '@element-plus/icons-vue'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import AppIcon from '@/components/common/AppIcon.vue'
|
||||
@@ -116,11 +119,14 @@ async function saveSecret(field: PluginSettingField) {
|
||||
} catch (reason) { feedback(message(reason, t('密钥保存失败', 'Failed to save secret'))) } finally { busy.value = '' }
|
||||
}
|
||||
async function deleteSecret(field: PluginSettingField) {
|
||||
if (!confirm(t('删除已保存的', 'Delete saved ') + field.label + '?')) return
|
||||
const pluginId = props.plugin.plugin_id
|
||||
if (!(await askConfirm(t('删除已保存的', 'Delete saved ') + field.label + '?'))) return
|
||||
if (pluginId !== props.plugin.plugin_id) return
|
||||
busy.value = 'secret:' + field.key
|
||||
feedback()
|
||||
try {
|
||||
const state = await pluginService.deletePluginSecret(props.plugin.plugin_id, field.key)
|
||||
const state = await pluginService.deletePluginSecret(pluginId, field.key)
|
||||
if (pluginId !== props.plugin.plugin_id) return
|
||||
if (schema.value) schema.value.secrets[field.key] = { configured: state.configured }
|
||||
secrets.value[field.key] = ''
|
||||
notice.value = field.label + t('已删除。', ' deleted.')
|
||||
@@ -130,6 +136,7 @@ async function deleteSecret(field: PluginSettingField) {
|
||||
|
||||
<template>
|
||||
<section class="mcp-panel">
|
||||
<ActionDialog v-if="actionDialog" v-bind="actionDialog" @resolve="resolveAction" />
|
||||
<nav class="mcp-tabs" :aria-label="t('MCP 与 Plugin 配置', 'MCP and Plugin settings')">
|
||||
<button v-for="tab in tabs" :key="tab.id" :class="{ active: activeTab === tab.id }" @click="selectTab(tab.id)">{{ tab.label }}</button>
|
||||
</nav>
|
||||
|
||||
@@ -54,13 +54,17 @@ it.each(['save', 'delete'] as const)('ignores old secret %s responses after swit
|
||||
let finish!: () => void
|
||||
vi.mocked(service.putPluginSecret).mockReturnValue(new Promise(resolve => { finish = () => resolve({ plugin_id: 'demo', key: 'token', configured: true }) }))
|
||||
if (action === 'delete') {
|
||||
vi.stubGlobal('confirm', vi.fn(() => true))
|
||||
|
||||
vi.mocked(service.deletePluginSecret).mockReturnValue(new Promise(resolve => { finish = () => resolve({ plugin_id: 'demo', key: 'token', configured: false }) }))
|
||||
}
|
||||
wrapper = mount(PluginSettingsPanel, { props: { pluginId: 'demo' } })
|
||||
await flushPromises()
|
||||
await wrapper.get('input[type="password"]').setValue('old-fixture-value')
|
||||
await wrapper.get(action === 'save' ? '.secret-row button' : '.secret-row .danger').trigger('click')
|
||||
if (action === 'delete') {
|
||||
await wrapper.get('.action-dialog').trigger('submit')
|
||||
await flushPromises()
|
||||
}
|
||||
vi.mocked(service.getPluginSettings).mockResolvedValue(secretSchema(false))
|
||||
await wrapper.setProps({ pluginId: 'other' })
|
||||
await flushPromises()
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import ActionDialog from '@/components/common/ActionDialog.vue'
|
||||
import { useActionDialog } from '@/composables/useActionDialog'
|
||||
const { actionDialog, resolveAction, askConfirm } = useActionDialog()
|
||||
import { computed, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue'
|
||||
import type { PluginSettingsSchema, PluginSettingField } from '@/contracts'
|
||||
import {
|
||||
@@ -106,9 +109,10 @@ async function saveSecret(key: string) {
|
||||
|
||||
async function clearSecret(key: string) {
|
||||
if (!schema.value || isSaving.value) return
|
||||
if (!confirm(`确认删除 " ${key} " 的配置?`)) return
|
||||
const version = loadVersion
|
||||
const pluginId = props.pluginId
|
||||
if (!(await askConfirm(`确认删除 " ${key} " 的配置?`))) return
|
||||
if (version !== loadVersion || pluginId !== props.pluginId) return
|
||||
isSaving.value = true
|
||||
saveError.value = ''
|
||||
try {
|
||||
@@ -145,6 +149,7 @@ watch(() => props.pluginId, load)
|
||||
|
||||
<template>
|
||||
<div class="plugin-settings-panel">
|
||||
<ActionDialog v-if="actionDialog" v-bind="actionDialog" @resolve="resolveAction" />
|
||||
<div v-if="isLoading" class="loading">加载设置中…</div>
|
||||
|
||||
<template v-else-if="schema && schema.fields.length > 0">
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import ActionDialog from '@/components/common/ActionDialog.vue'
|
||||
import { useActionDialog } from '@/composables/useActionDialog'
|
||||
const { actionDialog, resolveAction, askConfirm } = useActionDialog()
|
||||
import { Connection } from '@element-plus/icons-vue'
|
||||
import AppIcon from '@/components/common/AppIcon.vue'
|
||||
import ExtensionInstallDialog from '@/components/common/ExtensionInstallDialog.vue'
|
||||
@@ -39,13 +42,13 @@ async function toggle(id: string, enabled: boolean) {
|
||||
}
|
||||
|
||||
async function grant(id: string, permissions: string[]) {
|
||||
if (!confirm(`${t('将授权:', 'Grant permissions: ')}${permissions.join(', ')}。${t('是否继续?', 'Continue?')}`)) return
|
||||
if (!(await askConfirm(`${t('将授权:', 'Grant permissions: ')}${permissions.join(', ')}。${t('是否继续?', 'Continue?')}`))) return
|
||||
try { await pluginStore.grantPermissions(id, permissions) }
|
||||
catch (error) { actionError.value = error instanceof Error ? error.message : t('授权失败', 'Authorization failed') }
|
||||
}
|
||||
|
||||
async function uninstall(id: string, name: string) {
|
||||
if (!confirm(t(`卸载「${name}」将移除其全部 Contribution,是否继续?`, `Uninstalling “${name}” removes all its contributions. Continue?`))) return
|
||||
if (!(await askConfirm(t(`卸载「${name}」将移除其全部 Contribution,是否继续?`, `Uninstalling “${name}” removes all its contributions. Continue?`)))) return
|
||||
try { await pluginStore.uninstallPlugin(id) }
|
||||
catch (error) { actionError.value = error instanceof Error ? error.message : t('卸载失败', 'Uninstall failed') }
|
||||
}
|
||||
@@ -61,6 +64,7 @@ const hasCommandContribution = computed(() =>
|
||||
|
||||
<template>
|
||||
<section class="feature-page">
|
||||
<ActionDialog v-if="actionDialog" v-bind="actionDialog" @resolve="resolveAction" />
|
||||
<ExtensionInstallDialog v-if="showInstall" kind="Plugin" :install="pluginStore.installPlugin" @close="showInstall = false" @installed="showInstall = false; actionError = ''" />
|
||||
<header class="feature-header">
|
||||
<div><h1>{{ t('Plugin 与 MCP', 'Plugins and MCP') }}</h1><p>{{ t('管理插件生命周期、MCP Host、权限和受控 Contribution。', 'Manage plugin lifecycles, MCP hosts, permissions, and controlled contributions.') }}</p></div>
|
||||
|
||||
Reference in New Issue
Block a user