feat: add EditorGridSizePicker and EditorSelectionFrame components; implement FreeCreateView for article management; introduce ArticleSelectionOptimizeService for optimizing article selections

This commit is contained in:
2026-04-06 22:18:55 +08:00
parent 08ace0a9e0
commit 1e844704e4
20 changed files with 3033 additions and 740 deletions
@@ -171,11 +171,30 @@ async function handlePublished(): Promise<void> {
]);
}
const isFreeCreateEmpty = computed(
() =>
detail.value?.source_type === "free_create" &&
title.value.trim() === "" &&
markdown.value.trim() === "",
);
const isFreeCreateInitiallyEmpty = computed(
() =>
detail.value?.source_type === "free_create" &&
initialTitle.value.trim() === "" &&
initialMarkdown.value.trim() === "",
);
function handleBack(): void {
if (saveMutation.isPending.value) {
return;
}
if (isFreeCreateEmpty.value) {
void cleanupEmptyFreeCreate();
return;
}
if (hasChanges.value) {
leaveModalOpen.value = true;
return;
@@ -184,6 +203,19 @@ function handleBack(): void {
navigateBack();
}
async function cleanupEmptyFreeCreate(): Promise<void> {
try {
await articlesApi.remove(articleId.value);
await Promise.all([
queryClient.invalidateQueries({ queryKey: ["articles"] }),
queryClient.invalidateQueries({ queryKey: ["workspace"] }),
]);
} catch {
// ignore cleanup errors
}
navigateBack();
}
function navigateBack(): void {
if (window.history.length > 1) {
void router.back();
@@ -199,6 +231,10 @@ function handleStay(): void {
function handleDiscardLeave(): void {
leaveModalOpen.value = false;
if (isFreeCreateInitiallyEmpty.value) {
void cleanupEmptyFreeCreate();
return;
}
navigateBack();
}
@@ -293,6 +329,7 @@ function serializePlatformSelection(platformIds: string[]): string {
<MilkdownProvider>
<ArticleEditorCanvas
:key="String(articleId)"
:article-id="articleId"
v-model:title="title"
v-model="markdown"
:disabled="editorLocked"