172 lines
6.1 KiB
Vue
172 lines
6.1 KiB
Vue
|
|
<script setup lang="ts">
|
||
|
|
import { PlusOutlined } from "@ant-design/icons-vue";
|
||
|
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/vue-query";
|
||
|
|
import { message, type TableColumnsType } from "ant-design-vue";
|
||
|
|
import type { ScheduleTask } from "@geo/shared-types";
|
||
|
|
import { computed, ref } from "vue";
|
||
|
|
import { useI18n } from "vue-i18n";
|
||
|
|
|
||
|
|
import ScheduleTaskModal from "@/components/ScheduleTaskModal.vue";
|
||
|
|
import { schedulesApi } from "@/lib/api";
|
||
|
|
import { formatDateTime } from "@/lib/display";
|
||
|
|
import { formatError } from "@/lib/errors";
|
||
|
|
import { formatPublishPlatformSummary } from "@/lib/publish-platforms";
|
||
|
|
|
||
|
|
const queryClient = useQueryClient();
|
||
|
|
const { t } = useI18n();
|
||
|
|
|
||
|
|
const modalOpen = ref(false);
|
||
|
|
const editingTask = ref<ScheduleTask | null>(null);
|
||
|
|
const page = ref(1);
|
||
|
|
const pageSize = ref(20);
|
||
|
|
|
||
|
|
const listParams = computed(() => ({
|
||
|
|
page: page.value,
|
||
|
|
page_size: pageSize.value,
|
||
|
|
}));
|
||
|
|
|
||
|
|
const listQuery = useQuery({
|
||
|
|
queryKey: computed(() => ["schedules", "list", listParams.value]),
|
||
|
|
queryFn: () => schedulesApi.list(listParams.value),
|
||
|
|
});
|
||
|
|
|
||
|
|
const columns = computed<TableColumnsType<ScheduleTask>>(() => [
|
||
|
|
{ title: t("custom.schedule.name"), dataIndex: "name", key: "name", width: 160 },
|
||
|
|
{ title: t("custom.schedule.rule"), dataIndex: "prompt_rule_name", key: "prompt_rule_name", width: 140 },
|
||
|
|
{ title: t("custom.schedule.cron"), dataIndex: "cron_expr", key: "cron_expr", width: 140 },
|
||
|
|
{ title: t("custom.schedule.platform"), dataIndex: "target_platform", key: "target_platform", width: 120 },
|
||
|
|
{ title: t("custom.schedule.brand"), dataIndex: "brand_name", key: "brand_name", width: 120 },
|
||
|
|
{ title: t("custom.schedule.nextRun"), dataIndex: "next_run_at", key: "next_run_at", width: 168 },
|
||
|
|
{ title: t("custom.promptRule.status"), dataIndex: "status", key: "status", width: 100 },
|
||
|
|
{ title: t("common.createdAt"), dataIndex: "created_at", key: "created_at", width: 168 },
|
||
|
|
{ title: t("common.actions"), key: "actions", width: 160, fixed: "right" },
|
||
|
|
]);
|
||
|
|
|
||
|
|
const removeMutation = useMutation({
|
||
|
|
mutationFn: (id: number) => schedulesApi.remove(id),
|
||
|
|
onSuccess: async () => {
|
||
|
|
message.success(t("custom.messages.scheduleDeleted"));
|
||
|
|
await queryClient.invalidateQueries({ queryKey: ["schedules"] });
|
||
|
|
},
|
||
|
|
onError: (error) => message.error(formatError(error)),
|
||
|
|
});
|
||
|
|
|
||
|
|
const toggleStatusMutation = useMutation({
|
||
|
|
mutationFn: ({ id, status }: { id: number; status: "enabled" | "disabled" }) =>
|
||
|
|
schedulesApi.toggleStatus(id, { status }),
|
||
|
|
onSuccess: async (_data, variables) => {
|
||
|
|
message.success(
|
||
|
|
variables.status === "enabled"
|
||
|
|
? t("custom.messages.scheduleEnabled")
|
||
|
|
: t("custom.messages.scheduleDisabled"),
|
||
|
|
);
|
||
|
|
await queryClient.invalidateQueries({ queryKey: ["schedules"] });
|
||
|
|
},
|
||
|
|
onError: (error) => message.error(formatError(error)),
|
||
|
|
});
|
||
|
|
|
||
|
|
function openModal(task?: ScheduleTask): void {
|
||
|
|
editingTask.value = task ?? null;
|
||
|
|
modalOpen.value = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
function handleTableChange(nextPage: number, nextPageSize: number): void {
|
||
|
|
page.value = nextPage;
|
||
|
|
pageSize.value = nextPageSize;
|
||
|
|
}
|
||
|
|
</script>
|
||
|
|
|
||
|
|
<template>
|
||
|
|
<div class="schedule-task-tab">
|
||
|
|
<div class="schedule-task-tab__head">
|
||
|
|
<span class="schedule-task-tab__count">
|
||
|
|
{{ t("templates.list.count", { count: listQuery.data.value?.total ?? 0 }) }}
|
||
|
|
</span>
|
||
|
|
<a-button type="primary" @click="openModal()">
|
||
|
|
<template #icon><PlusOutlined /></template>
|
||
|
|
{{ t("common.create") }}
|
||
|
|
</a-button>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<a-table
|
||
|
|
:columns="columns"
|
||
|
|
:data-source="listQuery.data.value?.items ?? []"
|
||
|
|
:loading="listQuery.isPending.value"
|
||
|
|
:pagination="{
|
||
|
|
current: page,
|
||
|
|
pageSize,
|
||
|
|
total: listQuery.data.value?.total ?? 0,
|
||
|
|
showSizeChanger: true,
|
||
|
|
}"
|
||
|
|
row-key="id"
|
||
|
|
size="small"
|
||
|
|
:scroll="{ x: 1200 }"
|
||
|
|
@change="(p: any) => handleTableChange(p.current, p.pageSize)"
|
||
|
|
>
|
||
|
|
<template #bodyCell="{ column, record }">
|
||
|
|
<template v-if="column.key === 'prompt_rule_name'">
|
||
|
|
{{ record.prompt_rule_name || "--" }}
|
||
|
|
</template>
|
||
|
|
<template v-else-if="column.key === 'target_platform'">
|
||
|
|
{{ formatPublishPlatformSummary(record.target_platform) }}
|
||
|
|
</template>
|
||
|
|
<template v-else-if="column.key === 'brand_name'">
|
||
|
|
{{ record.brand_name || "--" }}
|
||
|
|
</template>
|
||
|
|
<template v-else-if="column.key === 'next_run_at'">
|
||
|
|
{{ record.next_run_at ? formatDateTime(record.next_run_at) : "--" }}
|
||
|
|
</template>
|
||
|
|
<template v-else-if="column.key === 'status'">
|
||
|
|
<a-tag :color="record.status === 'enabled' ? 'green' : 'default'">
|
||
|
|
{{ record.status === "enabled" ? t("custom.schedule.enabled") : t("custom.schedule.disabled") }}
|
||
|
|
</a-tag>
|
||
|
|
</template>
|
||
|
|
<template v-else-if="column.key === 'created_at'">
|
||
|
|
{{ formatDateTime(record.created_at) }}
|
||
|
|
</template>
|
||
|
|
<template v-else-if="column.key === 'actions'">
|
||
|
|
<a-space :size="4">
|
||
|
|
<a-button type="link" size="small" @click="openModal(record)">
|
||
|
|
{{ t("common.edit") }}
|
||
|
|
</a-button>
|
||
|
|
<a-button
|
||
|
|
type="link"
|
||
|
|
size="small"
|
||
|
|
@click="toggleStatusMutation.mutate({
|
||
|
|
id: record.id,
|
||
|
|
status: record.status === 'enabled' ? 'disabled' : 'enabled',
|
||
|
|
})"
|
||
|
|
>
|
||
|
|
{{ record.status === "enabled" ? t("custom.schedule.disabled") : t("custom.schedule.enabled") }}
|
||
|
|
</a-button>
|
||
|
|
<a-popconfirm
|
||
|
|
:title="t('custom.schedule.deleteConfirm')"
|
||
|
|
@confirm="removeMutation.mutate(record.id)"
|
||
|
|
>
|
||
|
|
<a-button type="link" size="small" danger>
|
||
|
|
{{ t("common.delete") }}
|
||
|
|
</a-button>
|
||
|
|
</a-popconfirm>
|
||
|
|
</a-space>
|
||
|
|
</template>
|
||
|
|
</template>
|
||
|
|
</a-table>
|
||
|
|
|
||
|
|
<ScheduleTaskModal v-model:open="modalOpen" :task="editingTask" />
|
||
|
|
</div>
|
||
|
|
</template>
|
||
|
|
|
||
|
|
<style scoped>
|
||
|
|
.schedule-task-tab__head {
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
justify-content: space-between;
|
||
|
|
margin-bottom: 16px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.schedule-task-tab__count {
|
||
|
|
font-size: 13px;
|
||
|
|
color: #8c8c8c;
|
||
|
|
}
|
||
|
|
</style>
|