Browse Source

添加章节类型选择器

Kevin Jiang 3 years ago
parent
commit
330ab8c225

+ 23 - 7
src/views/report/ReportCreateView.vue

@@ -1,9 +1,23 @@
 <script setup lang="ts">
-import { PlusOutlined } from '@ant-design/icons-vue';
+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 ReportMetaTextInputVue from './components/report-create/ReportMetaTextInput.vue';
+import DynamicMeta from './components/dynamic-meta/DynamicMeta.vue';
+import ChapterTypeSelect from "./components/ChapterTypeSelect.vue";
+
+const metadata: Ref<any[]> = ref([]);
+
+function onMetaChange(data: any[]) {
+  metadata.value = data;
+}
+
+const chapterTypeSelectVisible = ref(false);
+
+function onChapterTypeSelected(type: string) {
+  console.log('selected chapter type is', type);
+}
 </script>
 
 <template>
@@ -13,17 +27,19 @@ import ReportMetaTextInputVue from './components/report-create/ReportMetaTextInp
       <div class="metadata-wrap">
         <RelationCompanyInput />
         <ReportCategory />
-        <ReportMetaTextInputVue />
-        <a-button>
-          <template #icon><plus-outlined /></template>
-          添加新项
-        </a-button>
+        <DynamicMeta @change="onMetaChange" />
       </div>
     </a-col>
     <a-col :span="12">
       Logo
     </a-col>
   </a-row>
+  <a-divider></a-divider>
+  <a-button @click="() => chapterTypeSelectVisible = true">
+    <plus-outlined />
+    添加章节
+  </a-button>
+  <ChapterTypeSelect @ok="onChapterTypeSelected" v-model:visible="chapterTypeSelectVisible" />
 </template>
 
 <style scoped lang="scss">

+ 57 - 0
src/views/report/components/ChapterTypeSelect.vue

@@ -0,0 +1,57 @@
+<script setup lang="ts">
+import { ref } from "vue";
+
+defineProps({
+  visible: {
+    type: Boolean,
+    required: true,
+    default: false,
+  }
+});
+
+const emits = defineEmits<{
+  (e: "update:visible", visible: boolean): void;
+  (e: "ok", vaule: string): void;
+}>();
+
+const categories = ref([
+  '立项背景与意义',
+  '国内外研究现状与发展趋势',
+  '项目主要研究内容/项目实施主要内容',
+  '关键技术与创新点/拟解决的关键问题',
+  '项目实施方案',
+  '自定义章节',
+]);
+
+const category = ref('');
+
+function onOk() {
+  emits("ok", category.value);
+  emits("update:visible", false);
+}
+
+function onCancel() {
+  emits("update:visible", false);
+}
+
+</script>
+
+<template>
+  <a-modal :visible="visible" title="选择章节类型" @ok="onOk" @cancel="onCancel">
+    <a-radio-group v-model:value="category">
+      <a-radio class="radio" :value="item" v-for="(item, index) in categories" :key="index">{{ item }}</a-radio>
+    </a-radio-group>
+    <a-space>
+      <div v-for="(item, index) in categories" :key="index">
+      </div>
+    </a-space>
+  </a-modal>
+</template>
+
+<style scoped lang="scss">
+.radio {
+  display: flex;
+  height: 2em;
+  line-height: 2em;
+}
+</style>

+ 77 - 0
src/views/report/components/dynamic-meta/DynamicMeta.vue

@@ -0,0 +1,77 @@
+<!-- 报告元数据,封面上的动态字段 -->
+<script setup lang="ts">
+import { defineAsyncComponent, ref, markRaw, type Ref } from "vue";
+import { PlusOutlined } from '@ant-design/icons-vue';
+import type ReportMetaTextInput from './ReportMetaTextInput.vue';
+
+type ComponentType = typeof ReportMetaTextInput;
+
+const components: Ref<ComponentType[]> = ref([]);
+const props: Ref<any[]> = ref([]);
+
+const emits = defineEmits<{
+  (e: "change", data: any[]): void;
+}>();
+
+function addTextComponent() {
+  const comp = markRaw(defineAsyncComponent(() => import('./ReportMetaTextInput.vue') ));
+  components.value.push(comp);
+  props.value.push({
+    name: '',
+    value: ''
+  });
+}
+
+function addDateComponent() {
+  const comp = markRaw(defineAsyncComponent(() => import('./ReportMetaDateInput.vue') ));
+  components.value.push(comp);
+  props.value.push({
+    name: '',
+    value: ''
+  });
+}
+
+function handleMenuClick(e: {key: string}) {
+  if (e.key == 'text') {
+    addTextComponent();
+  } else if (e.key === 'date') {
+    addDateComponent();
+  }
+}
+
+function onMetaCompChange() {
+  emits("change", props.value);
+}
+</script>
+
+<template>
+  <div class="dynamic-meta-wrap">
+    <component
+      :is="comp"
+      v-for="(comp, index) in components"
+      :key="index"
+      v-model:data="props[index]"
+      @update:data="onMetaCompChange"
+    />
+    <a-dropdown>
+    <template #overlay>
+      <a-menu @click="handleMenuClick">
+        <a-menu-item key="text">文本</a-menu-item>
+        <a-menu-item key="date">日期</a-menu-item>
+      </a-menu>
+    </template>
+    <a-button>
+      添加新项
+      <plus-outlined />
+    </a-button>
+  </a-dropdown>
+  </div>
+</template>
+
+<style scoped lang="scss">
+.dynamic-meta-wrap {
+  > * {
+    margin-top: 0.5em;
+  }
+}
+</style>

+ 71 - 0
src/views/report/components/dynamic-meta/ReportMetaDateInput.vue

@@ -0,0 +1,71 @@
+<!-- 报告元数据日期输入类型组件 -->
+
+<script setup lang="ts">
+import { onMounted, ref, type PropType } from "vue";
+import type { Dayjs } from 'dayjs';
+import dayjs from "dayjs";
+
+export interface Prop {
+  name: string;
+  value: string;
+}
+
+const props = defineProps({
+  data: {
+    type: Object as PropType<Prop>,
+    required: true,
+  }
+});
+
+const value = ref<Dayjs>();
+
+onMounted(() => {
+  const date = dayjs(props.data.value);
+  if (date.isValid()) {
+    value.value = date;
+  }
+});
+
+const emits = defineEmits<{
+  (e: "update:data", data: Prop): void;
+}>();
+
+function onNameChange(e: InputEvent) {
+  change((e.target as HTMLInputElement).value, props.data.value);
+}
+
+function onValueChange(_: any, dateString: string) {
+  change(props.data.name, dateString);
+}
+
+function change(name: string, value: string) {
+  emits("update:data", { name, value});
+}
+</script>
+
+<template>
+  <a-row type="flex" style="align-items: center">
+    <a-col class="name-wrap">
+      <a-space>
+        <a-input :value="data.name" @change="onNameChange" placeholder="字段名称" class="name" />
+        <span>:</span>
+      </a-space>
+    </a-col>
+    <a-col flex="1">
+      <a-date-picker v-model:value="value" @change="onValueChange" class="value" />
+    </a-col>
+  </a-row>
+</template>
+
+<style scoped lang="scss">
+.name-wrap {
+  width: 120px;
+
+  .name {
+    width: 100%
+  }
+}
+.value {
+  width: 100%;
+}
+</style>

+ 60 - 0
src/views/report/components/dynamic-meta/ReportMetaTextInput.vue

@@ -0,0 +1,60 @@
+<!-- 报告元数据文本输入类型组件 -->
+
+<script setup lang="ts">
+import type { PropType } from "vue";
+
+export interface Prop {
+  name: string;
+  value: string;
+}
+
+const props = defineProps({
+  data: {
+    type: Object as PropType<Prop>,
+    required: true,
+  }
+});
+
+const emits = defineEmits<{
+  (e: "update:data", data: Prop): void;
+}>();
+
+function onNameChange(e: InputEvent) {
+  change((e.target as HTMLInputElement).value, props.data.value);
+}
+
+function onValueChange(e: InputEvent) {
+  change(props.data.name, (e.target as HTMLInputElement).value);
+}
+
+function change(name: string, value: string) {
+  emits("update:data", { name, value});
+}
+</script>
+
+<template>
+  <a-row type="flex" style="align-items: center">
+    <a-col class="name-wrap">
+      <a-space>
+        <a-input :value="data.name" @change="onNameChange" placeholder="字段名称" class="name" />
+        <span>:</span>
+      </a-space>
+    </a-col>
+    <a-col flex="1">
+      <a-input :value="data.value" @change="onValueChange" placeholder="请输入字段值" class="value" />
+    </a-col>
+  </a-row>
+</template>
+
+<style scoped lang="scss">
+.name-wrap {
+  width: 120px;
+
+  .name {
+    width: 100%
+  }
+}
+.value {
+  width: 100%;
+}
+</style>

+ 1 - 1
src/views/report/components/report-create/RelationCompanyInput.vue

@@ -38,7 +38,7 @@ watch(value.value, () => {
 
 <template>
   <a-row type="flex" style="align-items: center">
-    <a-col style="width: 100px; text-align: right;">
+    <a-col style="width: 120px; text-align: right;">
       关联企业:
     </a-col>
     <a-col flex="1">

+ 1 - 1
src/views/report/components/report-create/ReportCategory.vue

@@ -6,7 +6,7 @@ const value = ref('');
 
 <template>
   <a-row type="flex" style="align-items: center">
-    <a-col style="width: 100px; text-align: right;">
+    <a-col style="width: 120px; text-align: right;">
       报告类型:
     </a-col>
     <a-col flex="1">

+ 0 - 18
src/views/report/components/report-create/ReportMetaTextInput.vue

@@ -1,18 +0,0 @@
-<!-- 报告元数据文本输入类型组件 -->
-
-<script setup lang="ts">
-import { ref } from "vue";
-
-const value = ref('');
-</script>
-
-<template>
-  <a-row type="flex" style="align-items: center">
-    <a-col style="width: 100px">
-      <a-space><a-input style="width: 100%" />:</a-space>
-    </a-col>
-    <a-col flex="1">
-      <a-input v-model="value" placeholder="请输入报告类型" />
-    </a-col>
-  </a-row>
-</template>