fix: agent list page style
This commit is contained in:
@@ -225,23 +225,75 @@ onUnmounted(() => {
|
|||||||
|
|
||||||
<TabsContent value="agents" class="mt-4">
|
<TabsContent value="agents" class="mt-4">
|
||||||
<div class="rounded-lg border bg-card overflow-x-auto hide-scrollbar">
|
<div class="rounded-lg border bg-card overflow-x-auto hide-scrollbar">
|
||||||
<div class="flex items-center gap-2 sm:gap-4 px-3 sm:px-4 py-2 border-b bg-muted/50 text-xs sm:text-sm text-muted-foreground font-medium min-w-[360px]">
|
<!-- 大屏表头 -->
|
||||||
|
<div class="hidden sm:flex items-center gap-2 sm:gap-4 px-3 sm:px-4 py-2 border-b bg-muted/50 text-xs sm:text-sm text-muted-foreground font-medium">
|
||||||
<span class="w-10 sm:w-12 shrink-0">ID</span>
|
<span class="w-10 sm:w-12 shrink-0">ID</span>
|
||||||
<span class="w-6 shrink-0"></span>
|
<span class="w-6 shrink-0"></span>
|
||||||
<span class="w-24 sm:w-32 shrink-0">名称</span>
|
<span class="w-24 sm:w-32 shrink-0">名称</span>
|
||||||
<span class="w-24 sm:w-28 shrink-0 hidden sm:block">IP</span>
|
<span class="w-24 sm:w-28 shrink-0">IP</span>
|
||||||
<span class="w-20 sm:w-32 shrink-0 hidden md:block">主机名</span>
|
<span class="w-20 sm:w-32 shrink-0 hidden md:block">主机名</span>
|
||||||
<span class="w-20 sm:w-36 shrink-0 hidden lg:block">版本</span>
|
<span class="w-20 sm:w-36 shrink-0 hidden lg:block">版本</span>
|
||||||
<span class="w-40 shrink-0 hidden xl:block">心跳时间</span>
|
<span class="w-40 shrink-0 hidden xl:block">心跳时间</span>
|
||||||
<span class="w-40 shrink-0 hidden xl:block">创建时间</span>
|
<span class="w-40 shrink-0 hidden xl:block">创建时间</span>
|
||||||
<span class="w-20 sm:w-36 shrink-0">操作</span>
|
<span class="flex-1 text-center">操作</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="divide-y min-w-[360px]">
|
<div class="divide-y">
|
||||||
<div v-if="filteredAgents.length === 0" class="text-center py-8 text-muted-foreground">
|
<div v-if="filteredAgents.length === 0" class="text-center py-8 text-muted-foreground">
|
||||||
<Server class="h-8 w-8 mx-auto mb-2 opacity-50" />
|
<Server class="h-8 w-8 mx-auto mb-2 opacity-50" />
|
||||||
{{ searchQuery ? '无匹配结果' : '暂无 Agent' }}
|
{{ searchQuery ? '无匹配结果' : '暂无 Agent' }}
|
||||||
</div>
|
</div>
|
||||||
<div v-for="agent in filteredAgents" :key="agent.id" class="flex items-center gap-2 sm:gap-4 px-3 sm:px-4 py-2 hover:bg-muted/50 transition-colors">
|
<!-- 小屏布局 -->
|
||||||
|
<div v-for="agent in filteredAgents" :key="agent.id" class="sm:hidden p-3 hover:bg-muted/50 transition-colors">
|
||||||
|
<div class="flex items-start justify-between mb-2">
|
||||||
|
<div class="flex items-center gap-2 flex-1 min-w-0">
|
||||||
|
<span class="text-xs text-muted-foreground shrink-0">#{{ agent.id }}</span>
|
||||||
|
<span class="relative flex h-2.5 w-2.5 shrink-0" :title="isOnline(agent) ? '在线' : '离线'">
|
||||||
|
<span v-if="isOnline(agent)" class="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75"></span>
|
||||||
|
<span :class="isOnline(agent) ? 'bg-green-500' : 'bg-gray-400'" class="relative inline-flex rounded-full h-2.5 w-2.5"></span>
|
||||||
|
</span>
|
||||||
|
<span class="font-medium text-sm truncate cursor-pointer hover:text-primary" @click="viewDetail(agent)" :title="agent.name">{{ agent.name }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-0.5 shrink-0 ml-2">
|
||||||
|
<Button variant="ghost" size="icon" class="h-7 w-7" @click="toggleEnabled(agent)" :title="agent.enabled ? '禁用' : '启用'">
|
||||||
|
<Power v-if="agent.enabled" class="h-3.5 w-3.5 text-green-600" />
|
||||||
|
<PowerOff v-else class="h-3.5 w-3.5 text-gray-400" />
|
||||||
|
</Button>
|
||||||
|
<Button variant="ghost" size="icon" class="h-7 w-7" @click="viewDetail(agent)" title="详情">
|
||||||
|
<Eye class="h-3.5 w-3.5" />
|
||||||
|
</Button>
|
||||||
|
<Button variant="ghost" size="icon" class="h-7 w-7" @click="viewTasks(agent)" title="查看任务">
|
||||||
|
<ListTodo class="h-3.5 w-3.5" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-1 text-xs text-muted-foreground">
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<span class="w-12 shrink-0">IP:</span>
|
||||||
|
<span class="truncate">{{ agent.ip || '-' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<span class="w-12 shrink-0">主机:</span>
|
||||||
|
<span class="truncate">{{ agent.hostname || '-' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<span class="w-12 shrink-0">版本:</span>
|
||||||
|
<span class="truncate">{{ agent.version || '-' }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-1 mt-2 pt-2 border-t">
|
||||||
|
<Button variant="ghost" size="sm" class="h-7 text-xs" @click="forceUpdate(agent)">
|
||||||
|
<RotateCw class="h-3 w-3 mr-1" />更新
|
||||||
|
</Button>
|
||||||
|
<Button variant="ghost" size="sm" class="h-7 text-xs" @click="openEditDialog(agent)">
|
||||||
|
<Edit class="h-3 w-3 mr-1" />编辑
|
||||||
|
</Button>
|
||||||
|
<Button variant="ghost" size="sm" class="h-7 text-xs text-destructive" @click="confirmDelete(agent)">
|
||||||
|
<Trash2 class="h-3 w-3 mr-1" />删除
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 大屏布局 -->
|
||||||
|
<div v-for="agent in filteredAgents" :key="`desktop-${agent.id}`" class="hidden sm:flex items-center gap-2 sm:gap-4 px-3 sm:px-4 py-2 hover:bg-muted/50 transition-colors">
|
||||||
<span class="w-10 sm:w-12 shrink-0 text-muted-foreground text-xs sm:text-sm">#{{ agent.id }}</span>
|
<span class="w-10 sm:w-12 shrink-0 text-muted-foreground text-xs sm:text-sm">#{{ agent.id }}</span>
|
||||||
<span class="w-6 shrink-0 flex justify-center">
|
<span class="w-6 shrink-0 flex justify-center">
|
||||||
<span class="relative flex h-2.5 w-2.5" :title="isOnline(agent) ? '在线' : '离线'">
|
<span class="relative flex h-2.5 w-2.5" :title="isOnline(agent) ? '在线' : '离线'">
|
||||||
@@ -250,12 +302,12 @@ onUnmounted(() => {
|
|||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<span class="w-24 sm:w-32 shrink-0 font-medium text-xs sm:text-sm truncate cursor-pointer hover:text-primary" @click="viewDetail(agent)" :title="agent.name">{{ agent.name }}</span>
|
<span class="w-24 sm:w-32 shrink-0 font-medium text-xs sm:text-sm truncate cursor-pointer hover:text-primary" @click="viewDetail(agent)" :title="agent.name">{{ agent.name }}</span>
|
||||||
<span class="w-24 sm:w-28 shrink-0 text-xs sm:text-sm text-muted-foreground truncate hidden sm:block">{{ agent.ip || '-' }}</span>
|
<span class="w-24 sm:w-28 shrink-0 text-xs sm:text-sm text-muted-foreground truncate">{{ agent.ip || '-' }}</span>
|
||||||
<span class="w-20 sm:w-32 shrink-0 text-xs sm:text-sm text-muted-foreground truncate hidden md:block">{{ agent.hostname || '-' }}</span>
|
<span class="w-20 sm:w-32 shrink-0 text-xs sm:text-sm text-muted-foreground truncate hidden md:block">{{ agent.hostname || '-' }}</span>
|
||||||
<span class="w-20 sm:w-36 shrink-0 text-xs sm:text-sm text-muted-foreground truncate hidden lg:block">{{ agent.version || '-' }}</span>
|
<span class="w-20 sm:w-36 shrink-0 text-xs sm:text-sm text-muted-foreground truncate hidden lg:block">{{ agent.version || '-' }}</span>
|
||||||
<span class="w-40 shrink-0 text-xs sm:text-sm text-muted-foreground hidden xl:block">{{ agent.last_seen || '-' }}</span>
|
<span class="w-40 shrink-0 text-xs sm:text-sm text-muted-foreground hidden xl:block">{{ agent.last_seen || '-' }}</span>
|
||||||
<span class="w-40 shrink-0 text-xs sm:text-sm text-muted-foreground hidden xl:block">{{ agent.created_at || '-' }}</span>
|
<span class="w-40 shrink-0 text-xs sm:text-sm text-muted-foreground hidden xl:block">{{ agent.created_at || '-' }}</span>
|
||||||
<span class="w-20 sm:w-36 shrink-0 flex justify-end gap-0.5 sm:gap-1">
|
<span class="flex-1 flex justify-end gap-0.5 sm:gap-1">
|
||||||
<Button variant="ghost" size="icon" class="h-6 w-6 sm:h-7 sm:w-7" @click="toggleEnabled(agent)" :title="agent.enabled ? '禁用' : '启用'">
|
<Button variant="ghost" size="icon" class="h-6 w-6 sm:h-7 sm:w-7" @click="toggleEnabled(agent)" :title="agent.enabled ? '禁用' : '启用'">
|
||||||
<Power v-if="agent.enabled" class="h-3 w-3 sm:h-3.5 sm:w-3.5 text-green-600" />
|
<Power v-if="agent.enabled" class="h-3 w-3 sm:h-3.5 sm:w-3.5 text-green-600" />
|
||||||
<PowerOff v-else class="h-3 w-3 sm:h-3.5 sm:w-3.5 text-gray-400" />
|
<PowerOff v-else class="h-3 w-3 sm:h-3.5 sm:w-3.5 text-gray-400" />
|
||||||
|
|||||||
@@ -43,6 +43,16 @@ const allAgents = ref<Agent[]>([])
|
|||||||
const selectedEnvIds = ref<number[]>([])
|
const selectedEnvIds = ref<number[]>([])
|
||||||
const selectedAgentId = ref<string>('local')
|
const selectedAgentId = ref<string>('local')
|
||||||
const envSearchQuery = ref('')
|
const envSearchQuery = ref('')
|
||||||
|
// 为每个执行位置保存独立的工作目录配置
|
||||||
|
const workDirCache = ref<Record<string, string>>({})
|
||||||
|
|
||||||
|
// 当前显示的工作目录(根据选择的执行位置)
|
||||||
|
const currentWorkDir = computed({
|
||||||
|
get: () => workDirCache.value[selectedAgentId.value] || '',
|
||||||
|
set: (val) => {
|
||||||
|
workDirCache.value[selectedAgentId.value] = val
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const cleanConfig = computed(() => {
|
const cleanConfig = computed(() => {
|
||||||
if (!cleanType.value || cleanType.value === 'none' || cleanKeep.value <= 0) return ''
|
if (!cleanType.value || cleanType.value === 'none' || cleanKeep.value <= 0) return ''
|
||||||
@@ -90,21 +100,19 @@ watch(() => props.open, async (val) => {
|
|||||||
} else {
|
} else {
|
||||||
selectedEnvIds.value = []
|
selectedEnvIds.value = []
|
||||||
}
|
}
|
||||||
// 解析 Agent
|
// 解析 Agent 和工作目录
|
||||||
selectedAgentId.value = props.task?.agent_id ? String(props.task.agent_id) : 'local'
|
const agentId = props.task?.agent_id ? String(props.task.agent_id) : 'local'
|
||||||
|
selectedAgentId.value = agentId
|
||||||
|
// 初始化工作目录缓存,将当前任务的工作目录保存到对应的执行位置
|
||||||
|
workDirCache.value = {
|
||||||
|
[agentId]: props.task?.work_dir || ''
|
||||||
|
}
|
||||||
envSearchQuery.value = ''
|
envSearchQuery.value = ''
|
||||||
// 加载数据
|
// 加载数据
|
||||||
await loadData()
|
await loadData()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// 切换执行位置时,如果从本地切换到 Agent,清空工作目录
|
|
||||||
watch(selectedAgentId, (newVal, oldVal) => {
|
|
||||||
if (oldVal === 'local' && newVal !== 'local') {
|
|
||||||
form.value.work_dir = ''
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
async function loadData() {
|
async function loadData() {
|
||||||
try {
|
try {
|
||||||
const [envs, agents] = await Promise.all([
|
const [envs, agents] = await Promise.all([
|
||||||
@@ -134,6 +142,8 @@ async function save() {
|
|||||||
form.value.envs = selectedEnvIds.value.join(',')
|
form.value.envs = selectedEnvIds.value.join(',')
|
||||||
form.value.type = 'task'
|
form.value.type = 'task'
|
||||||
form.value.agent_id = selectedAgentId.value === 'local' ? null : Number(selectedAgentId.value)
|
form.value.agent_id = selectedAgentId.value === 'local' ? null : Number(selectedAgentId.value)
|
||||||
|
// 保存当前选择的执行位置对应的工作目录
|
||||||
|
form.value.work_dir = currentWorkDir.value
|
||||||
if (props.isEdit && form.value.id) {
|
if (props.isEdit && form.value.id) {
|
||||||
await api.tasks.update(form.value.id, form.value)
|
await api.tasks.update(form.value.id, form.value)
|
||||||
toast.success('任务已更新')
|
toast.success('任务已更新')
|
||||||
@@ -165,8 +175,8 @@ async function save() {
|
|||||||
<div class="grid grid-cols-1 sm:grid-cols-4 items-center gap-2 sm:gap-3">
|
<div class="grid grid-cols-1 sm:grid-cols-4 items-center gap-2 sm:gap-3">
|
||||||
<Label class="sm:text-right text-sm">工作目录</Label>
|
<Label class="sm:text-right text-sm">工作目录</Label>
|
||||||
<div class="sm:col-span-3">
|
<div class="sm:col-span-3">
|
||||||
<DirTreeSelect v-if="selectedAgentId === 'local'" :model-value="form.work_dir || ''" @update:model-value="v => form.work_dir = v" />
|
<DirTreeSelect v-if="selectedAgentId === 'local'" v-model="currentWorkDir" />
|
||||||
<Input v-else v-model="form.work_dir" placeholder="工作目录(可选)" class="h-8 text-sm" />
|
<Input v-else v-model="currentWorkDir" placeholder="工作目录(可选)" class="h-8 text-sm" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-1 sm:grid-cols-4 items-center gap-2 sm:gap-3">
|
<div class="grid grid-cols-1 sm:grid-cols-4 items-center gap-2 sm:gap-3">
|
||||||
|
|||||||
Reference in New Issue
Block a user