Skip to content

Commit d2e61ee

Browse files
authored
feat: added new plugin adding status and optimized added styles (#208)
1 parent 4c2328d commit d2e61ee

File tree

5 files changed

+92
-32
lines changed

5 files changed

+92
-32
lines changed

docs/demos/mcp-server-picker/basic-usage.vue

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -112,15 +112,15 @@ const installedPlugins = ref<PluginInfo[]>([
112112
},
113113
])
114114
115-
// 市场插件数据
115+
// 市场插件数据 - 演示三种不同的添加状态
116116
const marketPlugins = ref<PluginInfo[]>([
117117
{
118118
id: 'plugin-1',
119119
name: 'Jira 集成',
120120
icon: 'https://ts3.tc.mm.bing.net/th/id/ODLS.2a97aa8b-50c6-4e00-af97-3b563dfa07f4',
121121
description: 'Jira 任务管理',
122122
enabled: true,
123-
added: false,
123+
addState: 'idle', // 未添加状态,显示"添加"按钮
124124
tools: [
125125
{ id: 'tool-5', name: '创建任务', description: '创建 Jira 任务', enabled: false },
126126
{ id: 'tool-6', name: '查询任务', description: '查询 Jira 任务', enabled: false },
@@ -132,7 +132,7 @@ const marketPlugins = ref<PluginInfo[]>([
132132
icon: 'https://www.notion.so/front-static/favicon.ico',
133133
description: 'Notion 文档管理和协作',
134134
enabled: false,
135-
added: false,
135+
addState: 'loading', // 添加中状态,显示"添加中"按钮
136136
tools: [
137137
{ id: 'tool-7', name: '创建页面', description: '创建 Notion 页面', enabled: false },
138138
{ id: 'tool-8', name: '查询数据库', description: '查询 Notion 数据库', enabled: false },
@@ -144,7 +144,7 @@ const marketPlugins = ref<PluginInfo[]>([
144144
icon: 'https://telegram.org/favicon.ico',
145145
description: 'Telegram 消息推送和自动化',
146146
enabled: false,
147-
added: false,
147+
addState: 'added', // 已添加状态,显示"已添加"按钮
148148
tools: [{ id: 'tool-9', name: '发送消息', description: '发送 Telegram 消息', enabled: false }],
149149
category: 'ai',
150150
},
@@ -170,33 +170,37 @@ const handlePluginToggle = (plugin: PluginInfo, enabled: boolean) => {
170170
plugin.enabled = enabled
171171
}
172172
173-
const handlePluginAdd = (plugin: PluginInfo, added: boolean) => {
173+
const handlePluginAdd = (plugin: PluginInfo) => {
174174
const targetPlugin = marketPlugins.value.find((p) => p.id === plugin.id)!
175-
targetPlugin.added = added
176175
177-
if (added) {
178-
// 如果是添加操作,创建新的插件副本并添加到已安装列表
176+
// 设置为加载状态
177+
targetPlugin.addState = 'loading'
178+
179+
// 模拟异步添加过程
180+
setTimeout(() => {
181+
// 添加成功后设置为已添加状态
182+
targetPlugin.addState = 'added'
183+
179184
const newPlugin: PluginInfo = {
180185
...plugin,
181186
id: `${plugin.id}-installed-${Date.now()}`, // 生成新的ID避免冲突
182187
enabled: false, // 新添加的插件默认不启用
183-
added: true,
188+
addState: 'added',
184189
}
185190
installedPlugins.value.push(newPlugin)
186-
} else {
187-
// 如果是取消添加操作,从已安装列表中移除
188-
const index = installedPlugins.value.findIndex((p) => p.name === plugin.name)
189-
if (index > -1) {
190-
installedPlugins.value.splice(index, 1)
191-
}
192-
}
191+
}, 2000) // 模拟2秒的网络延迟
193192
}
194193
195194
const handlePluginDelete = (plugin: PluginInfo) => {
196195
const index = installedPlugins.value.findIndex((p) => p.id === plugin.id)
197196
if (index > -1) {
198197
installedPlugins.value.splice(index, 1)
199198
}
199+
200+
const marketPlugin = marketPlugins.value.find((p) => p.name === plugin.name)
201+
if (marketPlugin) {
202+
marketPlugin.addState = 'idle'
203+
}
200204
}
201205
202206
const handleToolToggle = (plugin: PluginInfo, toolId: string, enabled: boolean) => {

docs/src/components/mcp-server-picker.md

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,40 @@ MCP Server Picker 组件是一个用于展示和管理插件的组件,支持
1010

1111
<demo vue="../../demos/mcp-server-picker/basic-usage.vue" />
1212

13+
### 插件添加状态
14+
15+
市场插件支持三种添加状态,提供更好的用户体验:
16+
17+
- **idle**: 未添加状态,显示"添加"按钮,用户可以点击添加
18+
- **loading**: 添加中状态,显示"添加中"按钮,按钮不可点击,适用于网络请求等异步操作
19+
- **added**: 已添加状态,显示"已添加"按钮,按钮不可点击
20+
21+
通过 `addState` 属性控制插件的添加状态,开发者可以在添加插件的异步过程中动态更新状态,提升用户体验。
22+
23+
#### 状态控制示例
24+
25+
```typescript
26+
const handlePluginAdd = (plugin: PluginInfo) => {
27+
const targetPlugin = marketPlugins.value.find((p) => p.id === plugin.id)!
28+
29+
// 设置为加载状态
30+
targetPlugin.addState = 'loading'
31+
32+
// 异步添加插件
33+
addPluginToServer(plugin)
34+
.then(() => {
35+
// 添加成功
36+
targetPlugin.addState = 'added'
37+
// 添加到已安装列表
38+
installedPlugins.value.push(newPlugin)
39+
})
40+
.catch(() => {
41+
// 添加失败,重置为idle状态,用户可以重新尝试
42+
targetPlugin.addState = 'idle'
43+
})
44+
}
45+
```
46+
1347
## 弹出方式
1448

1549
> MCP Server Picker 组件支持两种弹出方式, 即 `Fixed` 模式和 `Drawer` 模式,通过 `popupConfig` 配置对象统一管理
@@ -91,7 +125,7 @@ MCP Server Picker 组件是一个用于展示和管理插件的组件,支持
91125
|--------|------|------|
92126
| `plugin-toggle` | `(plugin: PluginInfo, enabled: boolean)` | 插件启用/禁用 |
93127
| `plugin-delete` | `(plugin: PluginInfo)` | 删除插件 |
94-
| `plugin-add` | `(plugin: PluginInfo, added: boolean)` | 市场插件添加/取消添加 |
128+
| `plugin-add` | `(plugin: PluginInfo)` | 市场插件添加 |
95129
| `plugin-create` | `(type: 'form' \| 'code', data: PluginCreationData)` | 插件创建 |
96130

97131
#### 工具操作
@@ -111,6 +145,8 @@ MCP Server Picker 组件是一个用于展示和管理插件的组件,支持
111145
插件信息类型:
112146

113147
```typescript
148+
type PluginAddState = 'idle' | 'loading' | 'added'
149+
114150
interface PluginInfo {
115151
id: string // 插件唯一标识
116152
name: string // 插件名称
@@ -119,7 +155,7 @@ interface PluginInfo {
119155
enabled: boolean // 是否启用
120156
expanded?: boolean // 是否展开
121157
tools: PluginTool[] // 工具列表
122-
added?: boolean // 市场插件添加状态(可选)
158+
addState?: PluginAddState // 市场插件添加状态(可选): 'idle' - 未添加, 'loading' - 添加中, 'added' - 已添加
123159
category?: string // 插件分类(可选,用于市场分类筛选)
124160
}
125161
```

packages/components/src/mcp-server-picker/components/PluginCard.vue

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import { TinySwitch, TinyPopconfirm } from '@opentiny/vue'
33
import { computed, ref } from 'vue'
44
import { IconDelete, IconArrowRight, IconArrowDown } from '@opentiny/tiny-robot-svgs'
5-
import type { PluginCardEmits, PluginCardProps } from '../index.type'
5+
import type { PluginCardEmits, PluginCardProps, PluginInfo } from '../index.type'
66
77
const props = withDefaults(defineProps<PluginCardProps>(), {
88
mode: 'installed',
@@ -69,11 +69,11 @@ const handleDelete = () => {
6969
}
7070
7171
// 市场插件添加状态
72-
const isAdded = computed(() => props.plugin.added || false)
72+
const addState = computed(() => props.plugin.addState || 'idle')
7373
74-
const handleAdd = () => {
75-
const newAddedState = !isAdded.value
76-
emit('add-plugin', newAddedState)
74+
const handleAdd = (plugin: PluginInfo) => {
75+
if (addState.value !== 'idle') return
76+
emit('add-plugin', plugin)
7777
}
7878
7979
const getHoverTitle = (isEnabled: boolean) => {
@@ -132,10 +132,15 @@ const getHoverTitle = (isEnabled: boolean) => {
132132
<slot name="add-button">
133133
<div
134134
class="plugin-card__add-button"
135-
:class="{ 'plugin-card__add-button--added': isAdded }"
136-
@click="handleAdd"
135+
:class="{
136+
'plugin-card__add-button--loading': addState === 'loading',
137+
'plugin-card__add-button--added': addState === 'added',
138+
}"
139+
@click="handleAdd(plugin)"
137140
>
138-
<span>{{ isAdded ? '已添加' : '添加' }}</span>
141+
<span v-if="addState === 'idle'">添加</span>
142+
<span v-else-if="addState === 'loading'">添加中</span>
143+
<span v-else>已添加</span>
139144
</div>
140145
</slot>
141146
</div>
@@ -314,8 +319,21 @@ const getHoverTitle = (isEnabled: boolean) => {
314319
cursor: pointer;
315320
box-sizing: border-box;
316321
322+
&--loading {
323+
width: 78px;
324+
background: #e6f4ff;
325+
border-color: #1476ff;
326+
color: #1476ff;
327+
cursor: not-allowed;
328+
}
329+
317330
&--added {
318331
width: 78px;
332+
color: #c2c2c2;
333+
background-color: #f0f0f0;
334+
border-color: #dbdbdb;
335+
fill: #c2c2c2;
336+
cursor: not-allowed;
319337
}
320338
}
321339
}

packages/components/src/mcp-server-picker/index.type.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ export interface PluginTool {
55
enabled: boolean
66
}
77

8+
export type PluginAddState = 'idle' | 'loading' | 'added'
9+
810
export interface PluginInfo {
911
id: string
1012
name: string
@@ -13,7 +15,7 @@ export interface PluginInfo {
1315
enabled: boolean
1416
expanded?: boolean
1517
tools: PluginTool[]
16-
added?: boolean
18+
addState?: PluginAddState
1719
category?: string
1820
}
1921

@@ -28,7 +30,7 @@ export interface PluginCardProps {
2830
export interface PluginCardEmits {
2931
(e: 'toggle-plugin', enabled: boolean): void
3032
(e: 'toggle-tool', toolId: string, enabled: boolean): void
31-
(e: 'add-plugin', added: boolean): void
33+
(e: 'add-plugin', plugin: PluginInfo): void
3234
(e: 'delete-plugin'): void
3335
}
3436

@@ -133,7 +135,7 @@ export interface McpServerPickerEmits {
133135
// 插件操作事件
134136
(e: 'plugin-toggle', plugin: PluginInfo, enabled: boolean): void
135137
(e: 'plugin-delete', plugin: PluginInfo): void
136-
(e: 'plugin-add', plugin: PluginInfo, added: boolean): void
138+
(e: 'plugin-add', plugin: PluginInfo): void
137139
(e: 'plugin-create', type: 'form' | 'code', data: PluginCreationData): void
138140

139141
// 工具操作事件

packages/components/src/mcp-server-picker/index.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -173,9 +173,9 @@ const handleDeletePlugin = (plugin: PluginInfo) => {
173173
emit('plugin-delete', plugin)
174174
}
175175
176-
const handleAddPlugin = (plugin: PluginInfo, added: boolean) => {
176+
const handleAddPlugin = (plugin: PluginInfo) => {
177177
if (!props.allowPluginAdd) return
178-
emit('plugin-add', plugin, added)
178+
emit('plugin-add', plugin)
179179
}
180180
181181
const showModal = ref(false)
@@ -373,7 +373,7 @@ const transitionName = computed(() => {
373373
mode="market"
374374
:expandable="false"
375375
:show-tool-count="false"
376-
@add-plugin="(added: boolean) => handleAddPlugin(plugin, added)"
376+
@add-plugin="handleAddPlugin"
377377
/>
378378
</template>
379379
</div>

0 commit comments

Comments
 (0)