feat(kol): return to source page after generation and poll while busy

- Add a ?source=workspace|templates query param when opening the
  generate page so the back button and the post-submit redirect return
  to the originating page; invalidate articles + workspace caches on
  success so the new article shows up immediately.
- Poll recent articles every 10s in WorkspaceView and TemplatesView
  while any article is in queued/pending/generating/running, and stop
  the timer once nothing is active.
- Mark the new article as generating inside the generation Submit
  transaction so the client sees the right status as soon as the task
  is queued.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-18 17:17:17 +08:00
parent fe4a4ffeaa
commit 6e77f72c53
5 changed files with 105 additions and 13 deletions
+32 -6
View File
@@ -2,7 +2,7 @@
import { computed, ref, watch } from "vue";
import { useRoute, useRouter } from "vue-router";
import { useI18n } from "vue-i18n";
import { useMutation, useQuery } from "@tanstack/vue-query";
import { useMutation, useQuery, useQueryClient } from "@tanstack/vue-query";
import { message } from "ant-design-vue";
import { LeftOutlined } from "@ant-design/icons-vue";
import { kolGenerateApi } from "@/lib/api";
@@ -14,12 +14,32 @@ import {
isKolVariableValueEmpty,
normalizeKolVariableDefinition,
} from "@/lib/kol-placeholders";
import { formatError } from "@/lib/errors";
const route = useRoute();
const router = useRouter();
const queryClient = useQueryClient();
const { t } = useI18n();
const subscriptionPromptId = computed(() => String(route.params.subscriptionPromptId));
const sourcePage = computed<"workspace" | "templates" | null>(() => {
if (route.query.source === "workspace") {
return "workspace";
}
if (route.query.source === "templates") {
return "templates";
}
return null;
});
function resolveReturnRoute():
| { name: "workspace" }
| { name: "articles-templates" } {
if (sourcePage.value === "workspace") {
return { name: "workspace" };
}
return { name: "articles-templates" };
}
const schemaQuery = useQuery({
queryKey: ["kol", "subscription-prompt", "schema", subscriptionPromptId],
@@ -64,17 +84,23 @@ const generateMutation = useMutation({
enable_web_search: allowWebSearch.value ? enableWebSearch.value : undefined,
knowledge_group_ids: allowUserKnowledge.value ? selectedKnowledgeGroupIds.value : undefined,
}),
onSuccess: (data) => {
message.success(t("common.copySuccess")); // Or a generic success message
void router.push({ name: "article-editor", params: { id: String(data.article_id) } });
onSuccess: async () => {
await Promise.all([
queryClient.invalidateQueries({ queryKey: ["articles"] }),
queryClient.invalidateQueries({ queryKey: ["workspace"] }),
]);
message.info(t("templates.wizard.messages.queued"));
await router.replace(resolveReturnRoute());
},
onError: (error: any) => {
message.error(error.message || t("common.noData"));
message.error(formatError(error) || t("common.noData"));
},
});
function handleBack() {
router.back();
void router.push(resolveReturnRoute());
}
function handleSubmit() {