216 lines
5.7 KiB
Vue
216 lines
5.7 KiB
Vue
<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> |