vue+arco design中封装一个支持单个多个上下换序需求的穿梭框

需求描述

在vue+arco的项目中做一个导出按钮前置弹窗,弹窗内含一个穿梭框,并且支持穿梭框内有默认选中字段并且可以自己调整字段左右及上下位置以便导出表格内字段位置显示

实现过程

因现在大部分ui库都有穿梭框  所以选择直接进行封装 为了方便 我把弹窗组件所有代码全部放入  按需取用

<template>
  <a-modal
    v-model:visible="exportVisible.visible"
    title-align="start"
    width="720px"
    title="导出"
  >
    <template #footer>
      <div style="text-align: center">
        <a-space :size="18">
          <a-button
            type="primary"
            :disabled="!exportVisible.expertId.length"
            @click="derive(1)"
          >
            导出当前列表
          </a-button>
          <a-button type="primary" @click="derive(2)">导出全部</a-button>
          <a-button type="outline" @click="derive(3)">取消</a-button>
        </a-space>
      </div>
    </template>
    <a-space fill :size="20">
      <a-transfer
        v-if="exportVisible.visible"
        v-model="exportVisible.expertId"
        v-model:selected="exportVisible.selectId"
        show-search
        :data="data"
        :source-input-search-props="{ placeholder: '请输入可选字段' }"
        :target-input-search-props="{ placeholder: '请输入已选导出字段' }"
      >
        <template #source-title> 可选字段 </template>
        <template #target-title> 已选导出字段 </template>
      </a-transfer>
      <a-space direction="vertical">
        <a-button
          :disabled="!targetSelect"
          shape="round"
          @click="() => upBtn()"
        >
          <template #icon>
            <icon-up />
          </template>
        </a-button>
        <a-button
          :disabled="!targetSelect"
          shape="round"
          @click="() => downBtn()"
        >
          <template #icon>
            <icon-down />
          </template>
        </a-button>
      </a-space>
    </a-space>
    <a-checkbox v-model="exportVisible.check" style="margin-top: 20px">
      多专家会诊病例按专家拆分
    </a-checkbox>
  </a-modal>
</template>

<script setup lang="ts">
  import { exportDynamic } from "@/api/diagnosis";
  import { Message } from "@arco-design/web-vue";
  import dayjs from "dayjs";

  defineOptions({ name: "ExpertModal" });

  const exportVisible = ref<any>({
    visible: false,
    allotStatus: 0,
    check: false,
    expertId: [],
    selectId: [],
    query: {},
  });
  const transfer = ref();
  const data = ref([]);
  const targetSelect = computed(() => {
    return (
      exportVisible.value.expertId.length &&
      exportVisible.value.expertId.some((item) =>
        exportVisible.value.selectId.includes(item),
      )
    );
  });
  const open = (params: any, cleck: any, query: any) => {
    data.value = params.value
      .map((item) => {
        return {
          value: item.dataIndex,
          label: item.title,
        };
      })
      .filter((item) => item.label !== "操作");
    exportVisible.value.query = query;
    if (cleck && cleck.length) {
      exportVisible.value.expertId = cleck;
    }
    exportVisible.value.visible = true;
  };
  const expertIdChange = (value) => {
    exportVisible.value.selectId = value;
  };
  // 向下移动
  const downBtn = () => {
    const expertId = exportVisible.value.expertId;
    const intersect = expertId.filter((item) =>
      exportVisible.value.selectId.includes(item),
    );
  
    intersect.forEach((item) => {
      const tempIndex = expertId.findIndex((i) => i === item);
      
      if (expertId[tempIndex + 1]) {
        const nextItem = expertId[tempIndex + 1];
        const dataTempIndex = data.value.findIndex((i) => i.value === item);
        const dataNextTempIndex = data.value.findIndex(
          (i) => i.value === nextItem,
        );

        // 右侧导入数组交换
        const expertIdItemToMove = expertId.splice(tempIndex, 1)[0];
        expertId.splice(tempIndex + 1, 0, expertIdItemToMove);
        // 原数组交换
        const itemToMove = data.value.splice(dataTempIndex, 1)[0];
        data.value.splice(dataNextTempIndex, 0, itemToMove);
      }
    });
  };
  // 向上移动
  const upBtn = () => {
    const expertId = exportVisible.value.expertId;
    const intersect = expertId.filter((item) =>
      exportVisible.value.selectId.includes(item),
    );

    intersect.forEach((item) => {
      const tempIndex = expertId.findIndex((i) => i === item);
      if (tempIndex) {
        const nextItem = expertId[tempIndex - 1];
        const dataTempIndex = data.value.findIndex((i) => i.value === item);
        const dataNextTempIndex = data.value.findIndex(
          (i) => i.value === nextItem,
        );

        // 右侧导入数组交换
        const expertIdItemToMove = expertId.splice(tempIndex, 1)[0];
        expertId.splice(tempIndex - 1, 0, expertIdItemToMove);
        // 原数组交换
        const itemToMove = data.value.splice(dataTempIndex, 1)[0];
        data.value.splice(dataNextTempIndex, 0, itemToMove);
      }
    });
  };
  const close = () => {
    exportVisible.value = {
      visible: false,
      allotStatus: 0,
      check: false,
      expertId: [],
      selectId: [],
      query: {},
    };
  };
  const derive = async (item) => {
    let params;
    if (item === 2||item === 1) {
      params = {
        consultationFieldInfos: exportVisible.value.expertId,
      };
    } else {
      close();
      return;
    }
    const res = await exportDynamic({
      ...params,
      isSplitByExpert: exportVisible.value.check,
      pageReqVO: item===1?{
        ...exportVisible.value.query,
      }:{
        siteStatus:exportVisible.value.query.siteStatus,
      },
    });
    if (res) {
      const elink = document.createElement("a");
      elink.download = `明细${dayjs().format('YYYY-MM-DD HH:mm')}.xlsx`;
      elink.style.display = "none";
      const blob = new Blob([res], { type: "application/x-msdownload" });
      elink.href = URL.createObjectURL(blob);
      document.body.appendChild(elink);
      elink.click();
      document.body.removeChild(elink);
      Message.success("导出成功");
      close();
    }
  };
  defineExpose({
    open,
  });
</script>

<style lang="less" scoped>
  :deep(.cps-transfer) {
    .cps-transfer-view {
      width: 280px;
      height: 500px;
    }
  }
</style>

外部打开弹窗及传参代码↓

  <!--导出-->
  <Export ref="exportRef" @done="search" />





 const exportRef = ref<InstanceType<typeof Export>>();
// 打开导出
  const exportData = () => {
    //默认1
    const DIAGNOSED = [
      "consultationNo",
      "pathologyType",
      "caseNo",
      "patientNickname",
      "patientAge",
      "sex",
      "deliveryDept",
      "applyDeptName",
      "consultationExpertName",
      "createTime",
      "consultationIdea",
      "consultationDeptName",
      "diagnoseTime",
    ];
    //  默认2
    const ARCHIVED = [
      "consultationNo",
      "pathologyType",
      "caseNo",
      "patientNickname",
      "patientAge",
      "sex",
      "deliveryDept",
      "applyDeptName",
      "consultationExpertName",
      "createTime",
      "consultationIdea",
      "consultationDeptName",
      "diagnoseTime",
      "archivedTime",
    ];
    const { time = [], ...rest } = query;
    const [startTime, endTime] = time;
    
    exportRef.value?.open(
      columns,//所有字段
      activeKey.value === TabEnum.DIAGNOSED ? DIAGNOSED : ARCHIVED,//打开弹窗默认已选字段
      {//外部列表筛选条件  导出当前列表所需要
        ...rest,
        siteStatus: unref(activeKey),
        startTime: startTime ? `${startTime} 00:00:00` : undefined,
        endTime: endTime ? `${endTime} 23:59:59` : undefined,
      },
    );
  };

注1:打开弹窗默认已选字段的顺序必须和所有字段里面字段的顺序位置一致 否则会导致上下切换出现乱序问题   例:全部字段:【one,two,three】  默认展示字段【two,three】  不可【three,two】

注2:在弹窗组件内备注的右侧导入数组交换实际和页面无关  正在导致页面内字段位置交换的是原数组交换部分  穿梭框组件只会根据原数组位置变化去变化  而右侧导入数组位置交换部分是为了在导出的时候把选中字段的顺序传给后端

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值