feat: connect template editor to APIs
This commit is contained in:
@@ -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;
|
||||
}
|
||||
@@ -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 };
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user