You've already forked agentic-coding-workflow
WEb fix
This commit is contained in:
@@ -33,14 +33,28 @@ const emit = defineEmits<{
|
||||
(e: 'cancel'): void
|
||||
}>()
|
||||
|
||||
// v-model 双向同步,但必须打破「W1 回填 → local 变 → W2 emit → 父 modelValue 变 → W1 再回填」
|
||||
// 的无限循环(曾导致表单任意输入/NSelect 选择即卡死浏览器)。
|
||||
// 关键:W2 emit 出去的就是 local 本身(同一引用),父组件直接持有该引用。当 W1 看到的
|
||||
// modelValue 与我们最近一次 emit 出去的值是同一个对象时,说明是回环而非真正的外部变更,
|
||||
// 必须忽略;否则(如编辑模式下父组件 onMounted 用全新对象回填)才同步进 local。
|
||||
const local = ref({ ...props.modelValue })
|
||||
let lastEmitted: typeof props.modelValue | undefined
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(v) => {
|
||||
if (v === lastEmitted) return
|
||||
local.value = { ...v }
|
||||
},
|
||||
)
|
||||
watch(local, (v) => emit('update:modelValue', v), { deep: true })
|
||||
watch(
|
||||
local,
|
||||
(v) => {
|
||||
lastEmitted = v
|
||||
emit('update:modelValue', v)
|
||||
},
|
||||
{ deep: true },
|
||||
)
|
||||
|
||||
const argsText = computed({
|
||||
get: () => local.value.args.join('\n'),
|
||||
@@ -52,7 +66,10 @@ const argsText = computed({
|
||||
const allowlistText = computed({
|
||||
get: () => (local.value.tool_allowlist ?? []).join('\n'),
|
||||
set: (v: string) => {
|
||||
local.value.tool_allowlist = v.split('\n').map((x) => x.trim()).filter((x) => x !== '')
|
||||
local.value.tool_allowlist = v
|
||||
.split('\n')
|
||||
.map((x) => x.trim())
|
||||
.filter((x) => x !== '')
|
||||
},
|
||||
})
|
||||
|
||||
@@ -192,30 +209,15 @@ function removeMcpServer(idx: number) {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<form
|
||||
class="space-y-4"
|
||||
@submit.prevent="emit('submit')"
|
||||
>
|
||||
<form class="space-y-4" @submit.prevent="emit('submit')">
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">Name</label>
|
||||
<NInput
|
||||
v-model:value="local.name"
|
||||
:readonly="isEdit"
|
||||
placeholder="claude_code"
|
||||
/>
|
||||
<p
|
||||
v-if="isEdit"
|
||||
class="text-xs text-gray-500 mt-1"
|
||||
>
|
||||
Name cannot be changed after creation
|
||||
</p>
|
||||
<NInput v-model:value="local.name" :readonly="isEdit" placeholder="claude_code" />
|
||||
<p v-if="isEdit" class="text-xs text-gray-500 mt-1">Name cannot be changed after creation</p>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">Display Name</label>
|
||||
<NInput
|
||||
v-model:value="local.display_name"
|
||||
placeholder="Claude Code"
|
||||
/>
|
||||
<NInput v-model:value="local.display_name" placeholder="Claude Code" />
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">Description</label>
|
||||
@@ -227,10 +229,7 @@ function removeMcpServer(idx: number) {
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">客户端类型</label>
|
||||
<NSelect
|
||||
v-model:value="local.client_type"
|
||||
:options="clientTypeOptions"
|
||||
/>
|
||||
<NSelect v-model:value="local.client_type" :options="clientTypeOptions" />
|
||||
<p class="text-xs text-gray-500 mt-1">
|
||||
决定 session 启动时注入的配置目录变量与下方配置文件表单
|
||||
</p>
|
||||
@@ -253,23 +252,12 @@ function removeMcpServer(idx: number) {
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">Env</label>
|
||||
<p
|
||||
v-if="isEdit && existingEnvKeys?.length"
|
||||
class="text-xs text-gray-500 mb-2"
|
||||
>
|
||||
Existing keys (values hidden): {{ existingEnvKeys.join(', ') }}.
|
||||
Edit below to replace entirely.
|
||||
<p v-if="isEdit && existingEnvKeys?.length" class="text-xs text-gray-500 mb-2">
|
||||
Existing keys (values hidden): {{ existingEnvKeys.join(', ') }}. Edit below to replace
|
||||
entirely.
|
||||
</p>
|
||||
<div
|
||||
v-for="(e, idx) in envEntries"
|
||||
:key="e.id"
|
||||
class="flex gap-2 mb-2"
|
||||
>
|
||||
<NInput
|
||||
v-model:value="e.k"
|
||||
placeholder="KEY"
|
||||
class="flex-1"
|
||||
/>
|
||||
<div v-for="(e, idx) in envEntries" :key="e.id" class="flex gap-2 mb-2">
|
||||
<NInput v-model:value="e.k" placeholder="KEY" class="flex-1" />
|
||||
<NInput
|
||||
v-model:value="e.v"
|
||||
type="password"
|
||||
@@ -277,20 +265,9 @@ function removeMcpServer(idx: number) {
|
||||
placeholder="value"
|
||||
class="flex-1"
|
||||
/>
|
||||
<NButton
|
||||
quaternary
|
||||
@click="removeEnv(idx)"
|
||||
>
|
||||
×
|
||||
</NButton>
|
||||
<NButton quaternary @click="removeEnv(idx)"> × </NButton>
|
||||
</div>
|
||||
<NButton
|
||||
size="small"
|
||||
dashed
|
||||
@click="addEnv"
|
||||
>
|
||||
+ Add
|
||||
</NButton>
|
||||
<NButton size="small" dashed @click="addEnv"> + Add </NButton>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">
|
||||
@@ -307,34 +284,14 @@ function removeMcpServer(idx: number) {
|
||||
<label class="block text-sm font-medium mb-1">
|
||||
第三方 MCP Servers(session 启动时随平台 MCP 一并下发;名称 acw 为平台保留)
|
||||
</label>
|
||||
<div
|
||||
v-for="(m, idx) in mcpRows"
|
||||
:key="m.id"
|
||||
class="border rounded p-3 mb-2 space-y-2"
|
||||
>
|
||||
<div v-for="(m, idx) in mcpRows" :key="m.id" class="border rounded p-3 mb-2 space-y-2">
|
||||
<div class="flex gap-2">
|
||||
<NInput
|
||||
v-model:value="m.name"
|
||||
placeholder="名称(如 context7)"
|
||||
class="flex-1"
|
||||
/>
|
||||
<NSelect
|
||||
v-model:value="m.type"
|
||||
:options="mcpTypeOptions"
|
||||
class="w-28"
|
||||
/>
|
||||
<NButton
|
||||
quaternary
|
||||
@click="removeMcpServer(idx)"
|
||||
>
|
||||
×
|
||||
</NButton>
|
||||
<NInput v-model:value="m.name" placeholder="名称(如 context7)" class="flex-1" />
|
||||
<NSelect v-model:value="m.type" :options="mcpTypeOptions" class="w-28" />
|
||||
<NButton quaternary @click="removeMcpServer(idx)"> × </NButton>
|
||||
</div>
|
||||
<template v-if="m.type === 'stdio'">
|
||||
<NInput
|
||||
v-model:value="m.command"
|
||||
placeholder="command(如 npx)"
|
||||
/>
|
||||
<NInput v-model:value="m.command" placeholder="command(如 npx)" />
|
||||
<NInput
|
||||
v-model:value="m.argsText"
|
||||
type="textarea"
|
||||
@@ -349,47 +306,26 @@ function removeMcpServer(idx: number) {
|
||||
/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<NInput
|
||||
v-model:value="m.url"
|
||||
placeholder="URL(如 https://mcp.example.com/mcp)"
|
||||
/>
|
||||
<NInput v-model:value="m.url" placeholder="URL(如 https://mcp.example.com/mcp)" />
|
||||
<NInput
|
||||
v-model:value="m.headersText"
|
||||
type="textarea"
|
||||
:autosize="{ minRows: 1, maxRows: 5 }"
|
||||
placeholder="headers(每行 KEY=VALUE,如 Authorization=Bearer xxx)"
|
||||
/>
|
||||
<p
|
||||
v-if="m.type === 'sse'"
|
||||
class="text-xs text-gray-500"
|
||||
>
|
||||
<p v-if="m.type === 'sse'" class="text-xs text-gray-500">
|
||||
注意:codex 不支持 sse 类型,会在启动时被跳过
|
||||
</p>
|
||||
</template>
|
||||
</div>
|
||||
<NButton
|
||||
size="small"
|
||||
dashed
|
||||
@click="addMcpServer"
|
||||
>
|
||||
+ 添加 MCP Server
|
||||
</NButton>
|
||||
<NButton size="small" dashed @click="addMcpServer"> + 添加 MCP Server </NButton>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<NCheckbox v-model:checked="local.enabled">
|
||||
Enabled
|
||||
</NCheckbox>
|
||||
<NCheckbox v-model:checked="local.enabled"> Enabled </NCheckbox>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<NButton
|
||||
type="primary"
|
||||
attr-type="submit"
|
||||
>
|
||||
Save
|
||||
</NButton>
|
||||
<NButton @click="emit('cancel')">
|
||||
Cancel
|
||||
</NButton>
|
||||
<NButton type="primary" attr-type="submit"> Save </NButton>
|
||||
<NButton @click="emit('cancel')"> Cancel </NButton>
|
||||
</div>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user