Files
agentic-coding-workflow/web/src/views/workspaces/components/RunProfileDialog.vue
T
2026-06-09 22:43:29 +08:00

207 lines
5.2 KiB
Vue

<template>
<NModal
:show="true"
preset="card"
:title="profile ? '编辑运行档' : '新建运行档'"
:style="{ width: '640px' }"
@close="emit('close')"
>
<NForm label-placement="top">
<div class="grid grid-cols-2 gap-3">
<NFormItem
label="Slug"
required
>
<NInput
v-model:value="form.slug"
placeholder="dev"
:disabled="!!profile"
/>
</NFormItem>
<NFormItem
label="名称"
required
>
<NInput
v-model:value="form.name"
placeholder="Dev server"
/>
</NFormItem>
</div>
<NFormItem label="描述">
<NInput
v-model:value="form.description"
type="textarea"
:autosize="{ minRows: 1, maxRows: 3 }"
/>
</NFormItem>
<NFormItem
label="启动命令"
required
>
<NInput
v-model:value="form.command"
placeholder="pnpm"
/>
</NFormItem>
<NFormItem label="参数(每行一个)">
<NInput
v-model:value="argsText"
type="textarea"
:autosize="{ minRows: 2, maxRows: 6 }"
placeholder="dev"
/>
</NFormItem>
<NFormItem label="环境变量">
<div class="w-full">
<div
v-if="profile && !editEnv"
class="text-sm text-gray-500 mb-2"
>
<span v-if="profile.env_keys.length">已配置{{ profile.env_keys.join(', ') }}</span>
<span v-else></span>
<NButton
text
type="primary"
size="small"
class="ml-2"
@click="enableEnvEdit"
>
修改
</NButton>
</div>
<NDynamicInput
v-else
v-model:value="envPairs"
preset="pair"
key-placeholder="KEY"
value-placeholder="value"
/>
</div>
</NFormItem>
<NFormItem>
<NCheckbox v-model:checked="form.enabled">
启用禁用后不可 start
</NCheckbox>
</NFormItem>
</NForm>
<template #footer>
<NSpace justify="end">
<NButton @click="emit('close')">
取消
</NButton>
<NButton
type="primary"
:loading="submitting"
@click="submit"
>
保存
</NButton>
</NSpace>
</template>
</NModal>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue'
import {
NModal,
NForm,
NFormItem,
NInput,
NDynamicInput,
NCheckbox,
NButton,
NSpace,
useMessage,
} from 'naive-ui'
import { ApiException } from '@/api/types'
import { useRunsStore } from '@/stores/runs'
import type { RunProfile, CreateRunProfileBody, UpdateRunProfileBody } from '@/api/runs'
const props = defineProps<{ wsId: string; profile?: RunProfile | null }>()
const emit = defineEmits<{ close: []; saved: [p: RunProfile] }>()
const store = useRunsStore()
const msg = useMessage()
const form = reactive({
slug: props.profile?.slug ?? '',
name: props.profile?.name ?? '',
description: props.profile?.description ?? '',
command: props.profile?.command ?? '',
enabled: props.profile?.enabled ?? true,
})
const argsText = ref<string>((props.profile?.args ?? []).join('\n'))
const envPairs = ref<{ key: string; value: string }[]>([])
// 编辑模式下默认不改 env(server 不回传明文值);点"修改"后才整体替换。
const editEnv = ref(!props.profile)
const submitting = ref(false)
function enableEnvEdit() {
editEnv.value = true
envPairs.value = []
}
function parsedArgs(): string[] {
return argsText.value
.split('\n')
.map((s) => s.trim())
.filter((s) => s.length > 0)
}
function parsedEnv(): Record<string, string> {
const env: Record<string, string> = {}
for (const p of envPairs.value) {
if (p.key.trim()) env[p.key.trim()] = p.value
}
return env
}
async function submit() {
if (!form.slug.trim() || !form.name.trim() || !form.command.trim()) {
msg.warning('slug / 名称 / 启动命令为必填')
return
}
submitting.value = true
try {
let saved: RunProfile
if (props.profile) {
const body: UpdateRunProfileBody = {
slug: form.slug,
name: form.name,
description: form.description,
command: form.command,
args: parsedArgs(),
enabled: form.enabled,
}
// 仅在用户显式修改时才整体替换 env,否则保留原值。
if (editEnv.value) body.env = parsedEnv()
saved = await store.update(props.profile.id, body)
} else {
const body: CreateRunProfileBody = {
slug: form.slug,
name: form.name,
description: form.description,
command: form.command,
args: parsedArgs(),
env: parsedEnv(),
enabled: form.enabled,
}
saved = await store.create(props.wsId, body)
}
emit('saved', saved)
emit('close')
} catch (e: unknown) {
msg.error('保存失败:' + (e instanceof ApiException ? e.body.message : String(e)))
} finally {
submitting.value = false
}
}
</script>