feat(deps): implement dependency auto-completion and interactive installation CLI (#147)
This commit is contained in:
@@ -360,7 +360,8 @@ export const api = {
|
||||
const query = new URLSearchParams({ language })
|
||||
if (lang_version) query.set('lang_version', lang_version)
|
||||
return request<Dependency[]>(`/deps/installed?${query}`)
|
||||
}
|
||||
},
|
||||
getInstallSuggestCmd: (logID: string) => request<{ command: string }>(`/deps/install-suggest-cmd?log_id=${logID}`)
|
||||
},
|
||||
agents: {
|
||||
list: () => request<Agent[]>('/agents'),
|
||||
|
||||
@@ -318,7 +318,7 @@ async function save() {
|
||||
|
||||
<template>
|
||||
<Dialog :open="open" @update:open="emit('update:open', $event)">
|
||||
<DialogContent class="max-w-[95vw] sm:max-w-[700px] xl:max-w-[950px] p-0 overflow-hidden border-none bg-background shadow-2xl transition-all duration-300" style="text-rendering: optimizeLegibility;" @openAutoFocus.prevent>
|
||||
<DialogContent class="max-w-[95vw] sm:max-w-[700px] xl:max-w-[950px] p-0 overflow-hidden border-none bg-background shadow-2xl transition-all duration-300" style="text-rendering: optimizeLegibility;" @openAutoFocus.prevent @pointerDownOutside.prevent>
|
||||
<div class="flex flex-col max-h-[85vh]">
|
||||
<DialogHeader class="px-5 sm:px-6 pr-20 pt-6 pb-2 shrink-0">
|
||||
<div class="flex flex-col sm:flex-row sm:items-center justify-between gap-4 sm:gap-2">
|
||||
|
||||
@@ -9,7 +9,7 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
|
||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||
import DirTreeSelect from '@/components/DirTreeSelect.vue'
|
||||
import { Plus, X, ChevronDown, Search, AlertCircle, Terminal, Zap, Lock, Variable } from 'lucide-vue-next'
|
||||
import { Plus, X, ChevronDown, Search, AlertCircle, Terminal, Zap, Lock, Variable, Wrench } from 'lucide-vue-next'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { api, type Task, type EnvVar, type Agent } from '@/api'
|
||||
@@ -283,7 +283,7 @@ async function save() {
|
||||
|
||||
<template>
|
||||
<Dialog :open="open" @update:open="emit('update:open', $event)">
|
||||
<DialogContent class="max-w-[95vw] sm:max-w-[600px] xl:max-w-[850px] p-0 overflow-hidden border-none bg-background shadow-2xl transition-all duration-300" style="text-rendering: optimizeLegibility;" @openAutoFocus.prevent>
|
||||
<DialogContent class="max-w-[95vw] sm:max-w-[600px] xl:max-w-[850px] p-0 overflow-hidden border-none bg-background shadow-2xl transition-all duration-300" style="text-rendering: optimizeLegibility;" @openAutoFocus.prevent @pointerDownOutside.prevent>
|
||||
<div class="flex flex-col max-h-[85vh]">
|
||||
<DialogHeader class="px-6 pr-12 pt-6 pb-2 shrink-0 border-b border-muted/50">
|
||||
<DialogTitle class="text-xl font-bold py-2">
|
||||
@@ -354,8 +354,15 @@ async function save() {
|
||||
<div class="grid grid-cols-1 sm:grid-cols-4 items-start gap-3">
|
||||
<div class="sm:col-span-1" />
|
||||
<div class="sm:col-span-3">
|
||||
<div class="flex items-center gap-2.5 p-3 rounded-xl bg-amber-500/5 border border-amber-500/10 text-amber-600 dark:text-amber-400 text-[11px] leading-relaxed font-medium">
|
||||
<AlertCircle class="h-4 w-4 shrink-0 text-amber-500" /><p>请先在<b>「语言依赖」</b>中安装所需的运行时。执行脚本时将自动注入该环境。</p>
|
||||
<div class="flex flex-col gap-2 p-3 rounded-xl bg-amber-500/5 border border-amber-500/10 text-amber-600 dark:text-amber-400 text-[11px] leading-relaxed font-medium">
|
||||
<div class="flex items-center gap-2.5">
|
||||
<AlertCircle class="h-4 w-4 shrink-0 text-amber-500" />
|
||||
<p>请先在<b>「语言依赖」</b>中安装所需的运行时。执行脚本时将自动注入该环境。</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-2.5 mt-1 border-t border-amber-500/10 pt-1.5">
|
||||
<Wrench class="h-4 w-4 shrink-0 text-amber-500" />
|
||||
<p><b>依赖自动补全:</b>若执行报错提示缺失包(如 requests 等),可在任务列表的<b>「操作菜单」</b>中点击<b>「补全依赖」</b>进行一键安装。</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,15 +2,17 @@
|
||||
import { ref, onMounted, computed, watch, onUnmounted } from 'vue'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import BaihuDialog from '@/components/ui/BaihuDialog.vue'
|
||||
import { Dialog, DialogContent } from '@/components/ui/dialog'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import Pagination from '@/components/Pagination.vue'
|
||||
import TaskDialog from './TaskDialog.vue'
|
||||
import RepoDialog from './RepoDialog.vue'
|
||||
import LogViewer from '@/views/history/LogViewer.vue'
|
||||
import { Plus, Play, Pencil, Trash2, Search, ScrollText, GitBranch, Terminal, Server, Monitor, X, Loader2, RefreshCw, Wifi, WifiOff, Zap, ZapOff, Copy, Tag, ChevronDown, Pin, PinOff, MoreHorizontal, CalendarClock } from 'lucide-vue-next'
|
||||
import { Plus, Play, Pencil, Trash2, Search, ScrollText, GitBranch, Terminal, Server, Monitor, X, Loader2, RefreshCw, Wifi, WifiOff, Zap, ZapOff, Copy, Tag, ChevronDown, Pin, PinOff, MoreHorizontal, CalendarClock, Wrench } from 'lucide-vue-next'
|
||||
import TagInput from '@/components/TagInput.vue'
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
|
||||
import XTerminal from '@/components/XTerminal.vue'
|
||||
import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||
import {
|
||||
DropdownMenu,
|
||||
@@ -416,6 +418,31 @@ async function viewLogs(taskId: string) {
|
||||
}
|
||||
}
|
||||
|
||||
const showTerminalDialog = ref(false)
|
||||
const terminalCmd = ref('')
|
||||
|
||||
async function handleTaskDepInstall(task: Task) {
|
||||
try {
|
||||
const res = await api.logs.list({ task_id: task.id, page: 1, page_size: 1 })
|
||||
if (res.data && res.data.length > 0) {
|
||||
const latestLog = res.data[0]
|
||||
if (latestLog) {
|
||||
const cmdRes = await api.deps.getInstallSuggestCmd(latestLog.id)
|
||||
if (cmdRes && cmdRes.command) {
|
||||
terminalCmd.value = cmdRes.command
|
||||
showTerminalDialog.value = true
|
||||
}
|
||||
} else {
|
||||
toast.error('未找到该任务的运行日志,无法分析依赖')
|
||||
}
|
||||
} else {
|
||||
toast.error('该任务尚未运行,无日志可分析')
|
||||
}
|
||||
} catch (err: any) {
|
||||
toast.error(err.message || '获取依赖安装命令失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 视图管理
|
||||
const taskViews = ref<any[]>([])
|
||||
const newViewName = ref('')
|
||||
@@ -797,6 +824,10 @@ watch(() => route.query.agent_id, (newVal: any) => {
|
||||
<PinOff v-else class="h-3.5 w-3.5 mr-2 text-primary" />
|
||||
<span>{{ task.pin_type === 'top' ? '取消置顶' : '置顶任务' }}</span>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem @click="handleTaskDepInstall(task)">
|
||||
<Wrench class="h-3.5 w-3.5 mr-2 text-amber-500" />
|
||||
<span>补全依赖</span>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem @click="duplicateTask(task)">
|
||||
<Copy class="h-3.5 w-3.5 mr-2" />
|
||||
<span>复制任务</span>
|
||||
@@ -881,6 +912,10 @@ watch(() => route.query.agent_id, (newVal: any) => {
|
||||
<PinOff v-else class="h-3.5 w-3.5 mr-2 text-primary" />
|
||||
<span>{{ task.pin_type === 'top' ? '取消置顶' : '置顶任务' }}</span>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem @click="handleTaskDepInstall(task)">
|
||||
<Wrench class="h-3.5 w-3.5 mr-2 text-amber-500" />
|
||||
<span>补全依赖</span>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem @click="duplicateTask(task)">
|
||||
<Copy class="h-3.5 w-3.5 mr-2" />
|
||||
<span>复制任务</span>
|
||||
@@ -1070,5 +1105,20 @@ watch(() => route.query.agent_id, (newVal: any) => {
|
||||
</Button>
|
||||
</template>
|
||||
</BaihuDialog>
|
||||
|
||||
<!-- 依赖补全终端 -->
|
||||
<Dialog v-model:open="showTerminalDialog">
|
||||
<DialogContent class="w-[calc(100%-1rem)] sm:max-w-[90vw] lg:max-w-4xl h-[60vh] sm:h-[80vh] flex flex-col p-0 overflow-hidden bg-[#1e1e1e] border-none shadow-2xl [&>button]:hidden">
|
||||
<div class="flex items-center justify-between px-3 py-2 border-b border-[#3c3c3c]">
|
||||
<span class="text-xs sm:text-sm font-medium text-gray-300">依赖补全终端</span>
|
||||
<Button variant="ghost" size="icon" class="h-6 w-6 text-gray-400 hover:text-white" @click="showTerminalDialog = false">
|
||||
<X class="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
<div class="flex-1 overflow-hidden">
|
||||
<XTerminal v-if="showTerminalDialog" :initial-command="terminalCmd" />
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user