feat: connect template editor to APIs

This commit is contained in:
zwt13703
2026-07-01 21:48:11 +08:00
parent 60bdd5f268
commit 5a24edc3b0
4 changed files with 98 additions and 7 deletions
+43
View File
@@ -0,0 +1,43 @@
import { apiClient } from "./base";
import type { BlockConfig } from "@/stores/templateStore";
export type TemplateDetailResponse = {
template: {
id: number;
name: string;
version: string;
};
preview_html: string;
blocks: Array<{
block_id: string;
block_type?: string;
type?: string;
text_preview?: string;
text?: string;
level?: number;
sort_order?: number;
table_rows?: number;
table_cols?: number;
}>;
tree: Array<Record<string, unknown>>;
configs: Record<string, BlockConfig>;
};
export async function getTemplateDetail(templateId: number) {
const response = await apiClient.get<{ data: TemplateDetailResponse; message: string }>(
`/templates/${templateId}`,
);
return response.data.data;
}
export async function saveTemplateBlockConfig(
templateId: number,
blockId: string,
config: BlockConfig,
) {
const response = await apiClient.post<{ data: BlockConfig; message: string }>(
`/templates/${templateId}/blocks/${blockId}/config`,
config,
);
return response.data.data;
}
+39 -6
View File
@@ -1,4 +1,5 @@
import { defineStore } from "pinia";
import { getTemplateDetail, saveTemplateBlockConfig } from "@/api/templates";
export type RegionType = "ai_generate" | "manual" | "fixed" | "table";
@@ -48,6 +49,21 @@ const mockPreviewHtml = `
</table>
`;
function normalizeTreeNode(node: Record<string, unknown>): TreeNode {
const id = String(node.id ?? node.block_id ?? "");
const name = String(node.name ?? node.text ?? node.text_preview ?? id);
const children = Array.isArray(node.children)
? node.children.map((child) => normalizeTreeNode(child as Record<string, unknown>))
: undefined;
return {
id,
name,
status: "empty",
children,
};
}
export const useTemplateStore = defineStore("template", {
state: () => ({
loading: false,
@@ -112,20 +128,37 @@ export const useTemplateStore = defineStore("template", {
configById: (state) => (blockId: string) => state.configs[blockId],
},
actions: {
async loadTemplate() {
async loadTemplate(templateId = 1) {
this.loading = true;
this.error = "";
try {
await new Promise((resolve) => window.setTimeout(resolve, 200));
} catch {
this.error = "模板加载失败";
const detail = await getTemplateDetail(templateId);
this.template = {
id: detail.template.id,
name: detail.template.name,
version: detail.template.version,
};
this.previewHtml = detail.preview_html;
this.blocks = detail.blocks.map((block) => ({
block_id: block.block_id,
block_type: (block.block_type ?? block.type ?? "paragraph") as TemplateBlock["block_type"],
text_preview: block.text_preview ?? block.text ?? "",
level: block.level ?? 0,
sort_order: block.sort_order ?? 0,
table_rows: block.table_rows,
table_cols: block.table_cols,
}));
this.tree = detail.tree.map((node) => normalizeTreeNode(node));
this.configs = detail.configs;
} catch (error) {
this.error = error instanceof Error ? error.message : "模板加载失败";
} finally {
this.loading = false;
}
},
async saveBlockConfig(blockId: string, config: BlockConfig) {
await new Promise((resolve) => window.setTimeout(resolve, 400));
this.configs[blockId] = { ...config };
const savedConfig = await saveTemplateBlockConfig(this.template.id, blockId, config);
this.configs[blockId] = { ...savedConfig };
},
},
});
+4 -1
View File
@@ -1,5 +1,6 @@
<script setup lang="ts">
import { onMounted } from "vue";
import { useRoute } from "vue-router";
import ConfigPanel from "@/components/TemplateEdit/ConfigPanel.vue";
import LeftMenu from "@/components/TemplateEdit/LeftMenu.vue";
import StructureTree from "@/components/TemplateEdit/StructureTree.vue";
@@ -8,9 +9,11 @@ import WordPreview from "@/components/TemplateEdit/WordPreview.vue";
import { useTemplateStore } from "@/stores/templateStore";
const templateStore = useTemplateStore();
const route = useRoute();
onMounted(() => {
templateStore.loadTemplate();
const templateId = Number(route.params.id || 1);
templateStore.loadTemplate(Number.isFinite(templateId) ? templateId : 1);
});
</script>