jcloud/dashboard/src2/pages/JobPage.vue
2025-04-22 02:17:16 +08:00

216 lines
5.7 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="p-5" v-if="job">
<AlertAddressableError
v-if="error"
class="mb-5"
:name="error.name"
:title="error.title"
@done="$resources.errors.reload()"
/>
<Button
:route="{
name:
object.pagetype === 'Site'
? '站点任务'
: `${object.pagetype} 详情任务`,
}"
>
<template #prefix>
<i-lucide-arrow-left class="inline-block h-4 w-4" />
</template>
所有任务
</Button>
<div class="mt-3">
<div>
<div class="flex items-center">
<h2 class="text-lg font-medium text-gray-900">{{ job.job_type }}</h2>
<Badge class="ml-2" :label="job.status" />
<div class="ml-auto flex items-center space-x-2">
<Button
@click="$resources.job.reload()"
:loading="$resources.job.get.loading"
>
<template #icon>
<i-lucide-refresh-ccw class="h-4 w-4" />
</template>
</Button>
<Dropdown v-if="dropdownOptions.length" :options="dropdownOptions">
<template v-slot="{ open }">
<Button>
<template #icon>
<i-lucide-more-horizontal class="h-4 w-4" />
</template>
</Button>
</template>
</Dropdown>
</div>
</div>
<div>
<div
class="mt-4 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-5"
>
<div>
<div class="text-sm font-medium text-gray-500">创建时间</div>
<div class="mt-2 text-sm text-gray-900">
{{ $format.date(job.creation, 'lll') }}
</div>
</div>
<div>
<div class="text-sm font-medium text-gray-500">创建者</div>
<div class="mt-2 text-sm text-gray-900">
{{ job.owner }}
</div>
</div>
<div>
<div class="text-sm font-medium text-gray-500">持续时间</div>
<div class="mt-2 text-sm text-gray-900">
{{ job.end ? $format.duration(job.duration) : '-' }}
</div>
</div>
<div>
<div class="text-sm font-medium text-gray-500">开始时间</div>
<div class="mt-2 text-sm text-gray-900">
{{ $format.date(job.start, 'lll') }}
</div>
</div>
<div>
<div class="text-sm font-medium text-gray-500">结束时间</div>
<div class="mt-2 text-sm text-gray-900">
{{ job.end ? $format.date(job.end, 'lll') : '-' }}
</div>
</div>
</div>
</div>
</div>
<div class="mt-8 space-y-4">
<JobStep v-for="step in job.steps" :step="step" :key="step.name" />
</div>
</div>
</div>
</template>
<script>
import { FeatherIcon, Tooltip } from 'jingrow-ui';
import AlertAddressableError from '../components/AlertAddressableError.vue';
import { duration } from '../utils/format';
import { getObject } from '../objects';
import JobStep from '../components/JobStep.vue';
export default {
name: 'JobPage',
props: ['id', 'objectType'],
components: { Tooltip, FeatherIcon, JobStep, AlertAddressableError },
resources: {
job() {
return {
type: 'document',
pagetype: 'Agent Job',
name: this.id,
transform(job) {
for (let step of job.steps) {
step.title = step.step_name;
step.duration = duration(step.duration);
step.isOpen =
this.job?.steps?.find((s) => s.name === step.name)?.isOpen ||
false;
}
// 在交付失败时,任何步骤都不会有输出
// 所以在第一个步骤中显示任务输出(错误)
if (job.status === 'Delivery Failure') {
job.steps[0].output = job.output;
}
return job;
},
onSuccess() {
this.lastLoaded = Date.now();
},
};
},
errors() {
return {
type: 'list',
cache: ['通知', '错误', '代理任务', this.id],
pagetype: 'Jcloud Notification',
auto: true,
fields: ['标题', '名称'],
filters: {
document_type: '代理任务',
document_name: this.id,
is_actionable: true,
class: '错误',
},
limit: 1,
orderBy: '创建时间 desc',
};
},
},
computed: {
object() {
return getObject(this.objectType);
},
job() {
return this.$resources.job.pg;
},
error() {
return this.$resources.errors?.data?.[0] ?? null;
},
dropdownOptions() {
return [
{
label: '在桌面查看',
icon: 'external-link',
condition: () => this.$team?.pg?.is_desk_user,
onClick: () => {
window.open(
`${window.location.protocol}//${window.location.host}/app/agent-job/${this.id}`,
'_blank',
);
},
},
].filter((option) => option.condition?.() ?? true);
},
},
mounted() {
this.$socket.emit('pg_subscribe', '代理任务', this.id);
this.$socket.on('agent_job_update', (data) => {
if (data.id === this.id) {
data.steps = data.steps.map((step) => {
step.title = step.step_name;
step.duration = duration(step.duration);
step.isOpen =
this.job?.steps?.find((s) => s.name === step.name)?.isOpen || false;
return step;
});
this.$resources.job.pg = {
...this.$resources.job.pg,
...data,
};
}
});
// 每分钟重新加载任务,以防套接字无法工作
this.reloadInterval = setInterval(() => {
this.reload();
}, 1000 * 60);
},
beforeUnmount() {
this.$socket.emit('pg_unsubscribe', '代理任务', this.id);
this.$socket.off('agent_job_update');
clearInterval(this.reloadInterval);
},
methods: {
reload() {
if (
!this.$resources.job.loading &&
// 如果任务加载时间超过5秒则重新加载
Date.now() - this.lastLoaded > 5000
) {
this.$resources.job.reload();
}
},
},
};
</script>