| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647 |
- <script setup lang="ts">
- // 脚本调用工具的进度(docs/design/17-run-script.md):已调用次数、脚本报告的进度、用时
- import type { ScriptProgressPayload } from '@agent-server/shared'
- defineProps<{ scripts: ScriptProgressPayload[] }>()
- const STATUS: Record<ScriptProgressPayload['status'], string> = { running: '脚本运行中', completed: '脚本已完成', failed: '脚本未正常结束', cancelled: '脚本已取消' }
- function elapsed(ms: number) {
- const s = Math.floor(ms / 1000)
- return s < 60 ? `${s} 秒` : `${Math.floor(s / 60)} 分 ${s % 60} 秒`
- }
- const pct = (p: ScriptProgressPayload) => (p.total ? Math.min(100, Math.round(((p.done ?? 0) / p.total) * 100)) : null)
- </script>
- <template>
- <div class="script-progress">
- <div v-for="p in scripts" :key="p.scriptId" class="row" :class="p.status">
- <div class="line">
- <span class="codicon" :class="p.status === 'running' ? 'codicon-loading codicon-modifier-spin' : p.status === 'completed' ? 'codicon-pass' : 'codicon-error'" />
- <span class="status">{{ STATUS[p.status] }}</span>
- <span class="title">{{ p.title }}</span>
- <span class="meta">
- 已调用工具 {{ p.calls }} 次<template v-if="p.failures">(失败 {{ p.failures }})</template>
- <template v-if="p.total"> · {{ p.done ?? 0 }} / {{ p.total }}</template>
- <template v-if="p.message"> · {{ p.message }}</template>
- · {{ elapsed(p.elapsedMs) }}
- </span>
- </div>
- <div v-if="pct(p) !== null" class="bar"><div class="fill" :style="{ width: `${pct(p)}%` }" /></div>
- </div>
- </div>
- </template>
- <style scoped>
- .script-progress { display: flex; flex-direction: column; gap: 6px; padding: 6px 0 0 30px; }
- .row { display: flex; flex-direction: column; gap: 4px; font-size: 12px; color: var(--app-secondary-foreground); }
- .line { display: flex; align-items: baseline; flex-wrap: wrap; gap: 0 6px; min-width: 0; }
- .line .codicon { font-size: 12px; align-self: center; }
- .row.completed .codicon { color: var(--app-success); }
- .row.failed .codicon, .row.cancelled .codicon { color: var(--app-danger); }
- .status { color: var(--app-primary-foreground); font-weight: 500; }
- .title { overflow-wrap: anywhere; }
- .meta { overflow-wrap: anywhere; }
- .bar { height: 3px; max-width: 320px; border-radius: 2px; background: var(--app-input-border); overflow: hidden; }
- .fill { height: 100%; background: var(--app-claude-orange); transition: width 0.3s; }
- </style>
|