Browse Source

手风琴基础组件

刘洋 9 tháng trước cách đây
mục cha
commit
4ddb1c13bf

+ 117 - 0
src/render/components/Accordion/index.vue

@@ -0,0 +1,117 @@
+<template>
+  <div class="accordion">
+    <div class="item" v-for="(item, index) in _arr">
+      <div
+        class="head flex items-center justify-between"
+        @click="toggleOpen(item, $event)"
+      >
+        <div class="title-box">
+          <span class="title">{{ item.title }}</span>
+        </div>
+        <DownOutlined class="collapse-icon" :class="{ rotate: item.open }" />
+      </div>
+      <div class="body" :style="{ height: item.height }">
+        <slot :name="item.name" />
+      </div>
+    </div>
+  </div>
+</template>
+<script name="Accordion" lang="ts" setup>
+import { ref, watch, nextTick } from "vue";
+import { DownOutlined } from "@ant-design/icons-vue";
+interface AccordionItem {
+  title: string;
+  name: string;
+  open?: boolean;
+}
+const props = withDefaults(
+  defineProps<{
+    arr: AccordionItem[];
+  }>(),
+  {
+    arr: () => [],
+  }
+);
+const _arr = ref<any>([]);
+_arr.value = props.arr.map((item: AccordionItem) => {
+  return {
+    ...item,
+    height: item.open ? "auto" : "0px",
+    open: item.open || true,
+  };
+});
+
+const toggleOpen = async (
+  item: AccordionItem & { height?: any; open?: boolean },
+  e: any
+) => {
+  const body = e.target.nextSibling;
+  if (item.open && item.height === "auto") {
+    let h = getComputedStyle(body).height;
+    item.height = h;
+    await new Promise((rs: any) => {
+      setTimeout(() => {
+        rs();
+      }, 0);
+    });
+  }
+  item.open = !item.open;
+  if (item.open) {
+    item.height = "auto";
+    await nextTick();
+    let comHeight = getComputedStyle(body).height;
+    item.height = "0px";
+    setTimeout(() => {
+      item.height = comHeight;
+    }, 0);
+  } else {
+    item.height = "0px";
+  }
+};
+
+watch(
+  () => props.arr,
+  () => {
+    _arr.value = props.arr.map((item: AccordionItem) => {
+      return {
+        ...item,
+        height: item.open ? "auto" : "0px",
+        open: item.open || true,
+      };
+    });
+  }
+);
+</script>
+<style lang="less" scoped>
+.accordion {
+  .item {
+    .head {
+      height: 32px;
+      border-top: 1px solid #e5e5e5;
+      border-bottom: 1px solid #e5e5e5;
+      background: linear-gradient(180deg, #ffffff 0%, #f2f3f5 100%);
+      padding: 0 16px;
+      .title-box {
+        .title {
+          color: @text-color1;
+          font-size: 14px;
+          font-weight: 500;
+        }
+      }
+      .collapse-icon {
+        color: #8c8c8c;
+        font-size: 12px;
+        transition: all 0.2s;
+        &.rotate {
+          rotate: 180deg;
+        }
+      }
+    }
+    .body {
+      background-color: red;
+      transition: all 0.2s;
+      overflow: hidden;
+    }
+  }
+}
+</style>

+ 2 - 0
src/render/components/register.ts

@@ -22,6 +22,7 @@ import {
 import MyModal from "./MyModal/index.vue";
 import FooterInfo from "./FooterInfo/index.vue";
 import SelectCourse from "./SelectCourse/index.vue";
+import Accordion from "./Accordion/index.vue";
 
 use([
   CanvasRenderer,
@@ -46,5 +47,6 @@ export default {
     Vue.component("MyModal", MyModal);
     Vue.component("FooterInfo", FooterInfo);
     Vue.component("SelectCourse", SelectCourse);
+    Vue.component("Accordion", Accordion);
   },
 };

+ 8 - 1
src/render/views/ScanManage/ImageView.vue

@@ -22,12 +22,19 @@
         </div>
       </div>
     </div>
-    <div class="info-wrap"></div>
+    <div class="info-wrap">
+      <Accordion :arr="arr">
+        <template #one>11111</template>
+      </Accordion>
+    </div>
     <div class="image-wrap"></div>
   </div>
 </template>
 <script name="ImageView" lang="ts" setup>
 import { ref, computed } from "vue";
+
+const arr = ref([{ title: "搜索条件", name: "one", open: true }]);
+
 const listType = ref("kemu");
 const chooseLevel = computed(() => {
   return listType.value === "kemu" ? 1 : listType.value === "jiqi" ? 2 : 3;