Browse Source

实现单个模板的编辑

Kevin Jiang 2 years ago
parent
commit
3cd2ba849a

+ 5 - 0
src/client/reportTemplate.client.ts

@@ -25,3 +25,8 @@ export async function remove(id: number): Promise<boolean> {
   const resp = await httpClient.delete<Response<boolean>>(_url(id))
   return resp.data.data
 }
+
+export async function get(id: number): Promise<ReportTemplateSave> {
+  const resp = await httpClient.get<Response<ReportTemplateSave>>(_url(id));
+  return resp.data.data;
+}

+ 10 - 5
src/router/index.ts

@@ -21,11 +21,6 @@ export const router = createRouter({
           name: "ReportList",
           component: () => import("../views/report/ReportListView.vue"),
         },
-        {
-          path: "template",
-          name: "ReportTemplate",
-          component: () => import("../views/reportTemplate/ReportTemplateListView.vue"),
-        },
         // {
         //   path: "create",
         //   name: "ReportCreate",
@@ -36,6 +31,16 @@ export const router = createRouter({
           name: "ReportEditor",
           component: () => import("../views/report/ReportEditorView.vue"),
         },
+        {
+          path: "template",
+          name: "ReportTemplate",
+          component: () => import("../views/reportTemplate/ReportTemplateListView.vue"),
+        },
+        {
+          path: "template/edit/:id",
+          name: "ReportTemplateEditor",
+          component: () => import("../views/reportTemplate/ReportTemplateEditorView.vue"),
+        },
       ]
     },
     {

+ 8 - 0
src/services/reportTemplate.service.ts

@@ -16,3 +16,11 @@ export async function list(field?: string, keyword?: string, page?: number) {
 export async function remove(id: number) {
   return reportTemplateClient.remove(id)
 }
+
+/**
+ * 通过报告模板ID获取报告模板内容
+ * @param id 报告模板ID
+ */
+export async function get(id: number) {
+  return reportTemplateClient.get(id)
+}

+ 7 - 2
src/views/report/components/ChapterEditor.vue

@@ -11,6 +11,11 @@ const props = defineProps({
   data: {
     type: Object as PropType<Chapter>,
     required: true
+  },
+  contentEditorVisible: {
+    type: Boolean,
+    required: false,
+    default: true,
   }
 });
 
@@ -113,10 +118,10 @@ function searchReference() {
         </a-dropdown>
       </a-col>
     </a-row>
-    <p>
+    <p v-if="contentEditorVisible">
       <ContentEditor :data="data.content" @change="onContentChange" />
     </p>
-    <div class="chapter-button-group">
+    <div class="chapter-button-group" v-if="contentEditorVisible">
       <a-space>
         <a-button @click="searchReference">查找参考文献</a-button>
         <a-button>自动生成</a-button>

+ 8 - 26
src/views/report/components/dynamic-meta/DynamicMeta.vue

@@ -16,11 +16,6 @@ const props = defineProps({
   }
 });
 
-// interface DynamicComponentProp {
-//   name: string;
-//   value: string;
-// }
-
 interface DynamicComponent {
   id: number;
   component: ComponentType;
@@ -33,17 +28,6 @@ const emits = defineEmits<{
   (e: "change", data: ReportMetadataItem[]): void;
 }>();
 
-// function addComponent(comp: ComponentType, index: number) {
-//   dynamicComponents.value.push({
-//     id: index,
-//     component: comp,
-//     prop: {
-//       name: '',
-//       value: '',
-//     }
-//   });
-// }
-
 function addTextComponent(index: number, prop: ReportMetadataItem = { name: '', value: '', type: 'text' }) {
   // prop = prop || { name: '', value: '', type: 'text' }
   dynamicComponents.value.push({
@@ -63,32 +47,30 @@ function addDateComponent(index: number, prop: ReportMetadataItem = { name: '',
 }
 
 function handleMenuClick(e: {key: string}) {
-  // let comp: ComponentType | null = null;
   const index = dynamicComponents.value.length+1;
   if (e.key == 'text') {
     addTextComponent(index);
-    // comp = markRaw(defineAsyncComponent(() => import('./ReportMetaTextInput.vue') ));
   } else if (e.key === 'date') {
     addDateComponent(index);
-    // comp = markRaw(defineAsyncComponent(() => import('./ReportMetaDateInput.vue') ));
   }
-  // if (comp) {
-  //   addComponent(comp, dynamicComponents.value.length+1);
-  // }
+}
+
+function emitChange() {
+  const props = dynamicComponents.value.map((item) => item.prop);
+  emits("change", props);
 }
 
 function deleteComponent(index: number) {
   dynamicComponents.value.splice(index, 1);
+  emitChange()
 }
 
 function onMetaCompChange() {
-  const props = dynamicComponents.value.map((item) => item.prop);
-  emits("change", props);
+  emitChange()
 }
 
 function onDragEnd() {
-  const props = dynamicComponents.value.map((item) => item.prop);
-  emits("change", props);
+  emitChange()
 }
 
 onMounted(() => {

+ 146 - 0
src/views/reportTemplate/ReportTemplateEditorView.vue

@@ -0,0 +1,146 @@
+<!-- 报告模板编辑 -->
+<script setup lang="ts">
+import { onMounted, ref, type Ref } from "vue";
+import { useRoute } from "vue-router";
+import { CompLog } from "@/helpers/log.helper";
+import { ChapterManager } from "@/models/report.model";
+import * as reportTemplateService from "@/services/reportTemplate.service"
+import type { Chapter, ChapterType, ReportMetadataItem, ReportTemplateSave } from "@/types/report.types";
+import { PlusOutlined } from "@ant-design/icons-vue";
+import PageHeader from '@/components/PageHeader.vue';
+import DynamicMeta from '../report/components/dynamic-meta/DynamicMeta.vue';
+import ChapterTypeSelect from "../report/components/ChapterTypeSelect.vue";
+import LogoComponent from "../report/components/LogoComponent.vue";
+import ChapterEditor from "../report/components/ChapterEditor.vue";
+import { message } from "ant-design-vue";
+
+const logError = CompLog.logErr("ReportTemplateEditorView")
+
+const route = useRoute()
+
+const reportTemplate = ref<ReportTemplateSave | null>(null);
+
+// 报告元数据
+const metadata: Ref<ReportMetadataItem[]> = ref([]);
+// 章节类型选择器可见状态
+const chapterTypeSelectVisible = ref(false);
+// 章节管理对象
+const chapterManager = new ChapterManager();
+// 正在保存状态
+const saveLoading = ref(false);
+
+function fetchReportTemplate(id: number) {
+  reportTemplateService.get(id).then((data) => {
+    reportTemplate.value = data;
+    chapterManager.setChapters(reportTemplate.value.chapters)
+  }).catch(logError)
+}
+
+function onMetaChange(data: ReportMetadataItem[]) {
+  console.log('metadata', data)
+  metadata.value = data;
+  if (reportTemplate.value) {
+    reportTemplate.value.metadata = metadata.value;
+  }
+}
+
+function onChapterTypeSelected(type: ChapterType) {
+  chapterManager.addChapter(type);
+}
+
+function onChapterChange(index: number, data: Chapter) {
+  chapterManager.setChapter(index, data);
+}
+
+function onChapterMoveUp(index: number) {
+  chapterManager.moveUp(index)
+}
+
+function onChapterMoveDown(index: number) {
+  chapterManager.moveDown(index)
+}
+
+function onChapterRemove(index: number) {
+  chapterManager.remove(index)
+}
+
+function onSave() {
+  if (reportTemplate.value) {
+    saveLoading.value = true
+    reportTemplateService.save(reportTemplate.value).then(() => {
+      message.success("保存成功")
+      saveLoading.value = false
+    }).catch((err) => {
+      logError(err)
+      saveLoading.value = false
+    });
+  }
+}
+
+onMounted(() => {
+  const id = parseInt(route.params.id as string)
+  if (id) {
+    fetchReportTemplate(id)
+  }
+})
+</script>
+
+<template>
+  <PageHeader title="报告模板编辑" />
+  <a-row :gutter="24" v-if="reportTemplate">
+    <a-col :span="12">
+      <div class="metadata-wrap">
+        <a-row type="flex" style="align-items: center">
+          <a-col style="width: 120px; text-align: right;">
+            报告模板名称:
+          </a-col>
+          <a-col flex="1">
+            <a-input
+              v-model:value="reportTemplate.name"
+            />
+          </a-col>
+        </a-row>
+        <DynamicMeta v-model:data="reportTemplate.metadata" @change="onMetaChange" />
+      </div>
+    </a-col>
+    <a-col :span="12">
+      <LogoComponent />
+    </a-col>
+  </a-row>
+  <a-divider />
+  <div class="chapter-wrap" v-if="reportTemplate">
+    <ChapterEditor
+      v-for="(item, index) in chapterManager.getChapters()"
+      :key="index"
+      :data="item"
+      :contentEditorVisible="false"
+      @change="onChapterChange(index, $event)"
+      @move-up="onChapterMoveUp(index)"
+      @move-down="onChapterMoveDown(index)"
+      @remove="onChapterRemove(index)"
+    />
+  </div>
+  <div class="add-chapter-wrap">
+    <a-button @click="() => chapterTypeSelectVisible = true">
+      <plus-outlined />
+      添加章节
+    </a-button>
+  </div>
+  <div class="bottom-button-group">
+    <a-space>
+      <a-button type="primary" @click="onSave" :loading="saveLoading">保存</a-button>
+    </a-space>
+  </div>
+  <ChapterTypeSelect @ok="onChapterTypeSelected" v-model:visible="chapterTypeSelectVisible" />
+</template>
+
+<style scoped lang="scss">
+
+.add-chapter-wrap {
+  margin-top: 3em;
+}
+
+.bottom-button-group {
+  margin-top: 5em;
+}
+</style>

+ 1 - 0
src/views/reportTemplate/ReportTemplateListView.vue

@@ -73,6 +73,7 @@ onMounted(() => {
     <SpinComponent :spinning="loading">
       <a-card v-for="(item, index) in templates" :key="index" :title="item.name" class="template-item">
         <template #extra>
+          <RouterLink :to="{ name: 'ReportTemplateEditor', params: { id: item.id }}">编辑</RouterLink>
           <a-button type="link">使用模板创建报告</a-button>
           <a-popconfirm
             :title="'确定删模板 ' + item.name"