Browse Source

完善报告列表

Kevin Jiang 2 years ago
parent
commit
82bac3a4b2

+ 8 - 4
src/client/company.client.ts

@@ -1,10 +1,9 @@
 import httpClient from "@/services/httpClient";
-import type { Company } from "@/types/company.types";
+import type { Company, CompanyAgg } from "@/types/company.types";
 import type { Response } from "@/types/response.types";
+import * as urlHelper from "@/helpers/url.helper"
 
-function _url(path: string) {
-  return `/gw/user${path}`;
-}
+const _url = urlHelper.withPrefix("/gw/user");
 
 export async function get(id: number): Promise<Company> {
   const resp = await httpClient.get<Response<Company>>(_url(`/company/${id}`));
@@ -21,3 +20,8 @@ export async function query(name: string, size: number = 10): Promise<Company[]>
   const resp = await httpClient.get<Response<Company[]>>(_url("/company"), { params });
   return resp.data.data;
 }
+
+export async function agg(): Promise<CompanyAgg[]> {
+  const resp = await httpClient.get<Response<CompanyAgg[]>>(_url("/company/agg"))
+  return resp.data.data;
+}

+ 23 - 0
src/client/reports.client.ts

@@ -0,0 +1,23 @@
+/// 报告列表请求客户端
+
+import { withPrefix } from "@/helpers/url.helper";
+import httpClient from "@/services/httpClient";
+import type { CompanyAgg } from "@/types/company.types";
+import type { ReportsResponse } from "@/types/report.types";
+import type { Response } from "@/types/response.types";
+
+const _url = withPrefix("/gw/user")
+
+export async function query(page: number, companyId?: number): Promise<ReportsResponse> {
+  const params: any = { page }
+  if (companyId) {
+    params['companyId'] = companyId;
+  }
+  const resp = await httpClient.get<Response<ReportsResponse>>(_url("/reports"), { params })
+  return resp.data.data
+}
+
+export async function agg(): Promise<CompanyAgg[]> {
+  const resp = await httpClient.get<Response<CompanyAgg[]>>(_url("/company/agg"))
+  return resp.data.data;
+}

+ 9 - 9
src/components/NavMenu.vue

@@ -56,15 +56,15 @@ function logout(e: MouseEvent) {
   e.stopPropagation();
 }
 
-function onCreateReport() {
-  if (authStore.currentUser) {
-    reportService.create(authStore.currentUser.id).then((report) => {
-      routeToReportEditor(report.id);
-    }).catch((err) => console.log(err));
-  } else {
-    routeToLogin();
-  }
-}
+// function onCreateReport() {
+//   if (authStore.currentUser) {
+//     reportService.create().then((id) => {
+//       routeToReportEditor(id);
+//     }).catch((err) => console.log(err));
+//   } else {
+//     routeToLogin();
+//   }
+// }
 </script>
 
 <template>

+ 7 - 0
src/helpers/url.helper.ts

@@ -0,0 +1,7 @@
+import _ from "lodash";
+
+export function withPrefix(prefix: string) {
+  return (path: string) => {
+    return _.trimEnd(prefix, "/") + "/" + _.trimStart(path, "/")
+  }
+}

+ 7 - 8
src/router/index.ts

@@ -1,7 +1,6 @@
 import { useAuthStore } from "@/stores/auth.store";
 import type { SearchRouteParam } from "@/types/route.type";
 import { createRouter, createWebHistory } from "vue-router";
-import ReportView from "../views/report/ReportListView.vue";
 import RouteView from "../views/RouteView.vue";
 
 export const router = createRouter({
@@ -19,19 +18,19 @@ export const router = createRouter({
       children: [
         {
           path: "index",
-          name: "ReportIndex",
-          component: ReportView,
+          name: "ReportList",
+          component: () => import("../views/report/ReportListView.vue"),
         },
         {
           path: "template",
           name: "ReportTemplate",
           component: () => import("../views/report/ReportTemplateView.vue"),
         },
-        {
-          path: "create",
-          name: "ReportCreate",
-          component: () => import("../views/report/ReportCreateView.vue"),
-        },
+        // {
+        //   path: "create",
+        //   name: "ReportCreate",
+        //   component: () => import("../views/report/ReportCreateView.vue"),
+        // },
         {
           path: "edit/:id",
           name: "ReportEditor",

+ 5 - 1
src/services/company.service.ts

@@ -10,4 +10,8 @@ export async function create(name: string) {
 
 export async function query(name: string, size: number = 0) {
   return companyClient.query(name, size);
-}
+}
+
+export async function agg() {
+  return companyClient.agg();
+}

+ 7 - 14
src/services/report.service.ts

@@ -2,23 +2,16 @@ import * as reportClient from "@/client/report.client";
 import type { ReportSaveRequest } from "@/types/report.types";
 
 export async function get(id: number): Promise<ReportSaveRequest> {
-  return await reportClient.get(id);
+  const resp = await reportClient.get(id);
+  if (!resp.metadata) {
+    resp.metadata = [];
+  }
+  return resp;
 }
 
-export async function create(userId: number): Promise<ReportSaveRequest> {
+export async function create(): Promise<number> {
   const id = await reportClient.create();
-
-  const report: ReportSaveRequest = {
-    id,
-    companyId: 0,
-    name: '',
-    logoPath: '',
-    metadata: {},
-    chapters: [],
-    userId,
-  };
-
-  return report;
+  return id;
 }
 
 export async function save(reportSaveRequest: ReportSaveRequest) {

+ 9 - 0
src/services/reports.service.ts

@@ -0,0 +1,9 @@
+import * as reportsClient from "@/client/reports.client"
+
+export async function query(page?: number, companyId?: number) {
+  return reportsClient.query(page || 1, companyId);
+}
+
+export async function agg() {
+  return reportsClient.agg();
+}

+ 7 - 1
src/types/company.types.ts

@@ -2,4 +2,10 @@ export interface Company {
   id: number;
   name: string;
   date: string;
-}
+}
+
+export interface CompanyAgg {
+  companyId: number;
+  name: string;
+  count: number;
+}

+ 24 - 7
src/types/report.types.ts

@@ -54,12 +54,29 @@ export interface ReportChapterRequest extends Chapter {
   id: number;
 }
 
-export interface ReportSaveRequest {
-    id: number;
-    companyId: number;
-    name: string;
-    logoPath: string;
-    metadata: {[index: string]: string};
+export interface ReportBasic {
+  id: number;
+  companyId: number;
+  companyName?: string;
+  name: string;
+  logoPath: string;
+  metadata: ReportMetadataItem[];
+  userId: number;
+  createAt?: string;
+  updateAt?: string;
+}
+
+export interface ReportSaveRequest extends ReportBasic {
+    // id: number;
+    // companyId: number;
+    // name: string;
+    // logoPath: string;
+    // metadata: ReportMetadataItem[];
     chapters: ReportChapterRequest[];
-    userId: number;
+    // userId: number;
+}
+
+export interface ReportsResponse {
+  total: number;
+  reports: ReportBasic[];
 }

+ 0 - 88
src/views/report/ReportCreateView.vue

@@ -1,88 +0,0 @@
-<script setup lang="ts">
-import { ref, type Ref } from "vue";
-import { PlusOutlined } from "@ant-design/icons-vue";
-import PageHeader from '@/components/PageHeader.vue';
-import RelationCompanyInput from './components/report-create/RelationCompanyInput.vue';
-import ReportCategory from './components/report-create/ReportCategory.vue';
-import DynamicMeta from './components/dynamic-meta/DynamicMeta.vue';
-import ChapterTypeSelect from "./components/ChapterTypeSelect.vue";
-import LogoComponent from "./components/LogoComponent.vue";
-import type { Chapter, ChapterType } from "@/types/report.types";
-import { ChapterManager } from "@/models/report.model";
-import ChapterEditor from "./components/ChapterEditor.vue";
-
-// 报告元数据
-const metadata: Ref<any[]> = ref([]);
-// 章节类型选择器可见状态
-const chapterTypeSelectVisible = ref(false);
-// 章节管理对象
-const chapterManager = ref(new ChapterManager());
-
-function onMetaChange(data: any[]) {
-  metadata.value = data;
-}
-
-function onChapterTypeSelected(type: ChapterType) {
-  chapterManager.value.addChapter(type);
-}
-
-function onChapterChange(index: number, data: Chapter) {
-  chapterManager.value.setChapter(index, data);
-}
-</script>
-
-<template>
-  <PageHeader title="报告撰写" />
-  <a-row :gutter="24">
-    <a-col :span="12">
-      <div class="metadata-wrap">
-        <RelationCompanyInput />
-        <ReportCategory />
-        <DynamicMeta @change="onMetaChange" />
-      </div>
-    </a-col>
-    <a-col :span="12">
-      <LogoComponent />
-    </a-col>
-  </a-row>
-  <a-divider />
-  <div class="chapter-wrap">
-    <ChapterEditor
-      v-for="(item, index) in chapterManager.getChapters()"
-      :key="index"
-      :data="item"
-      @change="onChapterChange(index, $event)"
-    />
-  </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">保存</a-button>
-      <a-button>预览</a-button>
-      <a-button>导出</a-button>
-      <a-button>保存模板</a-button>
-    </a-space>
-  </div>
-  <ChapterTypeSelect @ok="onChapterTypeSelected" v-model:visible="chapterTypeSelectVisible" />
-</template>
-
-<style scoped lang="scss">
-.metadata-wrap {
-  > * {
-    margin-top: 0.5em;
-  }
-}
-
-.add-chapter-wrap {
-  margin-top: 3em;
-}
-
-.bottom-button-group {
-  margin-top: 5em;
-}
-</style>

+ 16 - 10
src/views/report/ReportEditorView.vue

@@ -1,5 +1,5 @@
 <script setup lang="ts">
-import { computed, onMounted, ref, type Ref } from "vue";
+import { onMounted, ref, type Ref } from "vue";
 import { useRoute } from "vue-router";
 import { PlusOutlined } from "@ant-design/icons-vue";
 import PageHeader from '@/components/PageHeader.vue';
@@ -8,7 +8,7 @@ import ReportCategory from './components/report-create/ReportCategory.vue';
 import DynamicMeta from './components/dynamic-meta/DynamicMeta.vue';
 import ChapterTypeSelect from "./components/ChapterTypeSelect.vue";
 import LogoComponent from "./components/LogoComponent.vue";
-import type { Chapter, ChapterType, ReportSaveRequest } from "@/types/report.types";
+import type { Chapter, ChapterType, ReportMetadataItem, ReportSaveRequest } from "@/types/report.types";
 import { ChapterManager } from "@/models/report.model";
 import ChapterEditor from "./components/ChapterEditor.vue";
 import * as reportService from "@/services/report.service";
@@ -20,17 +20,18 @@ const route = useRoute();
 const report: Ref<ReportSaveRequest | null> = ref(null);
 const reportCategoryEl = ref<HTMLInputElement | null>(null);
 
-const companyId = computed(() => report.value?.companyId || 0)
-
 // 报告元数据
-const metadata: Ref<any[]> = ref([]);
+const metadata: Ref<ReportMetadataItem[]> = ref([]);
 // 章节类型选择器可见状态
 const chapterTypeSelectVisible = ref(false);
 // 章节管理对象
 const chapterManager = ref(new ChapterManager());
 
-function onMetaChange(data: any[]) {
+function onMetaChange(data: ReportMetadataItem[]) {
   metadata.value = data;
+  if (report.value) {
+    report.value.metadata = metadata.value;
+  }
 }
 
 function onChapterTypeSelected(type: ChapterType) {
@@ -54,6 +55,7 @@ function onCompanyChange(companyId: number) {
 const saveLoading = ref(false);
 
 function onSave() {
+  console.log('report', report.value);
   if (report.value) {
     const reportName = (report.value.name || '').trim();
     if (reportName == '') {
@@ -64,6 +66,10 @@ function onSave() {
     saveLoading.value = true;
     reportService.save(report.value).then(() => {
       saveLoading.value = false;
+      message.success("保存成功")
+    }).catch((err) => {
+      saveLoading.value = false;
+      CompLog.logErr(err);
     });
   }
 }
@@ -82,12 +88,12 @@ onMounted(() => {
 
 <template>
   <PageHeader title="报告撰写" />
-  <a-row :gutter="24">
+  <a-row :gutter="24" v-if="report">
     <a-col :span="12">
       <div class="metadata-wrap">
-        <RelationCompanyInput v-model:id="companyId" @change="onCompanyChange" />
-        <ReportCategory ref="reportCategoryEl" />
-        <DynamicMeta @change="onMetaChange" />
+        <RelationCompanyInput v-model:id="report.companyId" @change="onCompanyChange" />
+        <ReportCategory v-model="report.name" ref="reportCategoryEl" />
+        <DynamicMeta v-model:data="report.metadata" @change="onMetaChange" />
       </div>
     </a-col>
     <a-col :span="12">

+ 82 - 21
src/views/report/ReportListView.vue

@@ -1,44 +1,95 @@
 <script setup lang="ts">
-import { ref } from "vue";
+import { onMounted, h, ref } from "vue";
+import { LoadingOutlined, PlusOutlined } from '@ant-design/icons-vue';
 import PageHeader from '@/components/PageHeader.vue';
 import QueryBox from './components/report-list/QueryBox.vue';
 import CompanySelect from "./components/report-list/CompanySelect.vue";
 import ReportItem from './components/report-list/ReportItem.vue';
+import * as reportService from "@/services/report.service"
+import * as reportsService from "@/services/reports.service"
+import { CompLog } from "@/helpers/log.helper";
+import type { ReportBasic } from "@/types/report.types"
+import { routeToReportEditor } from "@/router";
 
 const currentPage = ref(1);
 
-const companies = ref([
-  {
-    label: "企业名称1",
-    tail: 3,
-    checked: false,
-  },
-  {
-    label: "企业名称2",
-    tail: 7,
-    checked: true,
-  },
-  {
-    label: "企业名称3",
-    tail: 12,
-    checked: false,
-  }
-]);
+interface CompanyAggProp {
+  id?: number;
+  label: string;
+  tail: number;
+  checked: boolean;
+}
+
+const companies = ref<CompanyAggProp[]>([]);
+
+const total = ref(0);
+
+const reports = ref<ReportBasic[]>([]);
+
+// 加载状态
+const spinning = ref(true);
+
+function fetchReports(companyId?: number) {
+  spinning.value = true;
+  reportsService.query(1, companyId).then((resp) => {
+    total.value = resp.total;
+    reports.value = resp.reports;
+    spinning.value = false;
+  }).catch((err) => {
+    CompLog.logErr(err);
+    spinning.value = false;
+  })
+}
 
 function onCompanySelectChange(event: {index: number, checked: boolean}) {
   console.log('company select change', event);
   const company = companies.value[event.index];
   company.checked = event.checked;
+
+  if (event.checked) {
+    fetchReports(company.id)
+  } else {
+    fetchReports()
+  }
 }
 
 function onPaginationChange(page: number, pageSize: number) {
   console.log('page', page, 'pageSize', pageSize);
 }
+
+function onCreateReport() {
+  reportService.create().then((id) => {
+    routeToReportEditor(id);
+  }).catch((err) => console.log(err));
+}
+
+onMounted(() => {
+  fetchReports()
+  // 左侧企业名录统计
+  reportsService.agg().then((aggs) => {
+    companies.value = aggs.map((agg) => {
+      return {
+        id: agg.companyId,
+        label: agg.name,
+        tail: agg.count,
+        checked: false,
+      }
+    });
+  }).catch(CompLog.logErr)
+});
 </script>
 
 <template>
   <div class="reports-wrap">
-    <PageHeader title="我的报告" />
+    <a-row type="flex" justify="space-between">
+      <a-col>
+        <PageHeader title="我的报告" />
+      </a-col>
+      <a-col>
+        <a-button type="primary" @click="onCreateReport"><plus-outlined />新建报告</a-button>
+      </a-col>
+    </a-row>
+
     <div class="query-box-wrap">
       <QueryBox />
     </div>
@@ -48,11 +99,17 @@ function onPaginationChange(page: number, pageSize: number) {
         <CompanySelect :data="companies" @change="onCompanySelectChange" />
       </a-col>
       <a-col :xxl="19" :xl="18" :lg="17" :span="16">
-        <ReportItem v-for="(i) in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]" :key="i" class="report-item" />
+        <a-spin
+          :spinning="spinning"
+          :indicator="h(LoadingOutlined, { style: { fontSize: '24px', }, spin: true, })"
+          class="spinning"
+        >
+          <ReportItem v-for="(report, index) in reports" :data="report" :key="index" class="report-item" />
+        </a-spin>
       </a-col>
     </a-row>
     <div class="pagination-wrap">
-      <a-pagination v-model:current="currentPage" :total="500" @change="onPaginationChange" />
+      <a-pagination v-model:current="currentPage" :total="total" @change="onPaginationChange" />
     </div>
   </div>
 </template>
@@ -81,6 +138,10 @@ function onPaginationChange(page: number, pageSize: number) {
         margin-top: 0;
       }
     }
+
+    .spinning {
+      width: 100%;
+    }
   }
 
   .pagination-wrap {

+ 60 - 19
src/views/report/components/dynamic-meta/DynamicMeta.vue

@@ -1,51 +1,80 @@
 <!-- 报告元数据,封面上的动态字段 -->
 <script setup lang="ts">
-import { defineAsyncComponent, ref, markRaw, type Ref } from "vue";
+import { defineAsyncComponent, ref, markRaw, type Ref, type PropType, onMounted } from "vue";
 import { PlusOutlined, DeleteOutlined } from '@ant-design/icons-vue';
 import draggable from 'vuedraggable'
 
 import type ReportMetaTextInput from './ReportMetaTextInput.vue';
+import type { ReportMetadataItem } from "@/types/report.types";
 
 type ComponentType = typeof ReportMetaTextInput;
 
-interface DynamicComponentProp {
-  name: string;
-  value: string;
-}
+const props = defineProps({
+  data: {
+    type: Array as PropType<ReportMetadataItem[]>,
+    required: true,
+  }
+});
+
+// interface DynamicComponentProp {
+//   name: string;
+//   value: string;
+// }
 
 interface DynamicComponent {
   id: number;
   component: ComponentType;
-  prop: DynamicComponentProp;
+  prop: ReportMetadataItem;
 }
 
 const dynamicComponents: Ref<DynamicComponent[]> = ref([]);
 
 const emits = defineEmits<{
-  (e: "change", data: any[]): void;
+  (e: "change", data: ReportMetadataItem[]): void;
 }>();
 
-function addComponent(comp: ComponentType, index: number) {
+// 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({
+    id: index,
+    component: markRaw(defineAsyncComponent(() => import('./ReportMetaTextInput.vue') )),
+    prop
+  });
+}
+
+function addDateComponent(index: number, prop: ReportMetadataItem = { name: '', value: '', type: 'date' }) {
+  // prop = prop || { name: '', value: '', type: 'date' }
   dynamicComponents.value.push({
     id: index,
-    component: comp,
-    prop: {
-      name: '',
-      value: ''
-    }
+    component: markRaw(defineAsyncComponent(() => import('./ReportMetaDateInput.vue') )),
+    prop
   });
 }
 
 function handleMenuClick(e: {key: string}) {
-  let comp: ComponentType | null = null;
+  // let comp: ComponentType | null = null;
+  const index = dynamicComponents.value.length+1;
   if (e.key == 'text') {
-    comp = markRaw(defineAsyncComponent(() => import('./ReportMetaTextInput.vue') ));
+    addTextComponent(index);
+    // comp = markRaw(defineAsyncComponent(() => import('./ReportMetaTextInput.vue') ));
   } else if (e.key === 'date') {
-    comp = markRaw(defineAsyncComponent(() => import('./ReportMetaDateInput.vue') ));
-  }
-  if (comp) {
-    addComponent(comp, dynamicComponents.value.length+1);
+    addDateComponent(index);
+    // comp = markRaw(defineAsyncComponent(() => import('./ReportMetaDateInput.vue') ));
   }
+  // if (comp) {
+  //   addComponent(comp, dynamicComponents.value.length+1);
+  // }
 }
 
 function deleteComponent(index: number) {
@@ -61,6 +90,18 @@ function onDragEnd() {
   const props = dynamicComponents.value.map((item) => item.prop);
   emits("change", props);
 }
+
+onMounted(() => {
+  if (props.data) {
+    props.data.forEach((metadataItem, index) => {
+      if (metadataItem.type == 'text') {
+        addTextComponent(index, metadataItem);
+      } else if (metadataItem.type == 'date') {
+        addDateComponent(index, metadataItem);
+      }
+    })
+  }
+});
 </script>
 
 <template>

+ 8 - 7
src/views/report/components/dynamic-meta/ReportMetaDateInput.vue

@@ -4,15 +4,16 @@
 import { onMounted, ref, type PropType } from "vue";
 import type { Dayjs } from 'dayjs';
 import dayjs from "dayjs";
+import type { ReportMetadataItem } from "@/types/report.types";
 
-export interface Prop {
-  name: string;
-  value: string;
-}
+// export interface Prop {
+//   name: string;
+//   value: string;
+// }
 
 const props = defineProps({
   data: {
-    type: Object as PropType<Prop>,
+    type: Object as PropType<ReportMetadataItem>,
     required: true,
   }
 });
@@ -27,7 +28,7 @@ onMounted(() => {
 });
 
 const emits = defineEmits<{
-  (e: "update:data", data: Prop): void;
+  (e: "update:data", data: ReportMetadataItem): void;
 }>();
 
 function onNameChange(e: InputEvent) {
@@ -39,7 +40,7 @@ function onValueChange(_: any, dateString: string) {
 }
 
 function change(name: string, value: string) {
-  emits("update:data", { name, value});
+  emits("update:data", { name, value, type: props.data.type });
 }
 </script>
 

+ 8 - 7
src/views/report/components/dynamic-meta/ReportMetaTextInput.vue

@@ -1,22 +1,23 @@
 <!-- 报告元数据文本输入类型组件 -->
 
 <script setup lang="ts">
+import type { ReportMetadataItem } from "@/types/report.types";
 import type { PropType } from "vue";
 
-export interface Prop {
-  name: string;
-  value: string;
-}
+// export interface Prop {
+//   name: string;
+//   value: string;
+// }
 
 const props = defineProps({
   data: {
-    type: Object as PropType<Prop>,
+    type: Object as PropType<ReportMetadataItem>,
     required: true,
   }
 });
 
 const emits = defineEmits<{
-  (e: "update:data", data: Prop): void;
+  (e: "update:data", data: ReportMetadataItem): void;
 }>();
 
 function onNameChange(e: InputEvent) {
@@ -28,7 +29,7 @@ function onValueChange(e: InputEvent) {
 }
 
 function change(name: string, value: string) {
-  emits("update:data", { name, value});
+  emits("update:data", { name, value, type: props.data.type });
 }
 </script>
 

+ 28 - 5
src/views/report/components/report-create/RelationCompanyInput.vue

@@ -1,5 +1,5 @@
 <script setup lang="ts">
-import { ref, watch, type Ref } from "vue";
+import { onMounted, ref, type Ref } from "vue";
 import { debounce } from "lodash";
 import { PlusOutlined } from '@ant-design/icons-vue';
 import * as companyService from "@/services/company.service";
@@ -13,7 +13,6 @@ const props = defineProps({
   id: {
     type: Number,
     required: true,
-    default: 0,
   }
 });
 
@@ -52,15 +51,39 @@ function onModalOk() {
   }).catch(CompLog.logError);
 }
 
-watch(
-  () => props.id,
-  (id: number) => {
+// watch(
+//   () => props.id,
+//   (id: number) => {
+//     console.log('id', id);
+//     if (id > 0) {
+//       companyService.get(id).then((company) => {
+//         companyId.value = id;
+//         name.value = company.name;
+//       }).catch(CompLog.logError);
+//     }
+//   }
+// );
+
+onMounted(() => {
+  const id = props.id;
   if (id > 0) {
     companyService.get(id).then((company) => {
       companyId.value = id;
       name.value = company.name;
     }).catch(CompLog.logError);
   }
+  // watch(
+  //   () => props.id,
+  //   (id: number) => {
+  //     console.log('id', id);
+  //     if (id > 0) {
+  //       companyService.get(id).then((company) => {
+  //         companyId.value = id;
+  //         name.value = company.name;
+  //       }).catch(CompLog.logError);
+  //     }
+  //   }
+  // );
 });
 </script>
 

+ 13 - 2
src/views/report/components/report-create/ReportCategory.vue

@@ -1,7 +1,13 @@
 <script setup lang="ts">
 import { ref } from "vue";
 
-const value = ref('');
+defineProps({
+  modelValue: String,
+});
+
+const emits = defineEmits<{
+  (e: "update:modelValue", value: string): void;
+}>();
 
 const input = ref<HTMLInputElement | null>(null);
 
@@ -20,7 +26,12 @@ defineExpose({
       报告类型:
     </a-col>
     <a-col flex="1">
-      <a-input v-model="value" placeholder="请输入报告类型" ref="input" />
+      <a-input
+        :value="modelValue"
+        @input="emits('update:modelValue', $event.target.value)"
+        placeholder="请输入报告类型"
+        ref="input"
+      />
     </a-col>
   </a-row>
 </template>

+ 22 - 6
src/views/report/components/report-list/ReportItem.vue

@@ -1,18 +1,34 @@
 <!-- 报告列表的一项 -->
+<script setup lang="ts">
+import type { ReportBasic } from '@/types/report.types';
+import { computed, type PropType } from 'vue';
+
+const props = defineProps({
+  data: {
+    type: Object as PropType<ReportBasic>,
+    required: true,
+  }
+});
+
+const title = computed(() => props.data.companyName + ' - ' + props.data.name)
+</script>
 
 <template>
-  <a-card size="small" title="企业名称 - 报告名称">
+  <a-card size="small" :title="title">
     <template #extra>
-      <a-button type="link">编辑</a-button>
+      <RouterLink :to="{ name: 'ReportEditor', params: { id: data.id } }">编辑</RouterLink>
       <a-button type="link">下载</a-button>
     </template>
     <div class="report-metadata-wrap">
-      <p><span>企业名称:</span><span></span></p>
-      <p><span>项目名称:</span><span></span></p>
-      <p><span>项目负责人:</span><span></span></p>
+      <p><span>企业名称:</span><span>{{ data.companyName }}</span></p>
+      <p v-for="(item, index) in data.metadata" :key="index">
+        <span>{{ item.name }}:</span>
+        <span>{{ item.value }}</span>
+      </p>
+      <!-- <p><span>项目负责人:</span><span></span></p>
       <p><span>起止时间:</span><span></span></p>
       <p><span>创建时间:</span><span></span></p>
-      <p><span>最后编辑时间:</span><span></span></p>
+      <p><span>最后编辑时间:</span><span></span></p> -->
     </div>
   </a-card>
 </template>

+ 10 - 10
src/views/search/SearchResultView.vue

@@ -301,16 +301,16 @@ onUnmounted(() => {
       </a-col>
       <a-col :span="19">
         <a-spin :spinning="spinning" :indicator="h(LoadingOutlined, { style: { fontSize: '24px', }, spin: true, })">
-        <SearchResultToolbar
-          v-if="searchResult"
-          :data="searchResult"
-          v-model:sort="sort"
-          v-model:pagination="pagination"
-          @update:sort="changeSort"
-          @paginationChange="onPaginationChange"
-          @modeChange="onModeChange"
-        />
-        <SearchResultList :data="searchResult" :mode="resultMode" v-if="searchResult" />
+          <SearchResultToolbar
+            v-if="searchResult"
+            :data="searchResult"
+            v-model:sort="sort"
+            v-model:pagination="pagination"
+            @update:sort="changeSort"
+            @paginationChange="onPaginationChange"
+            @modeChange="onModeChange"
+          />
+          <SearchResultList :data="searchResult" :mode="resultMode" v-if="searchResult" />
         </a-spin>
       </a-col>
     </a-row>