TransferTableModal.vue 11.1 KB
<script lang="ts" setup>
  import { Button, Tabs, Tag } from 'ant-design-vue';
  import { remove, uniqBy, cloneDeep } from 'lodash';
  import { computed, nextTick, onMounted, ref, unref, toRaw } from 'vue';
  import {
    deviceTableColumn,
    deviceTableFormSchema,
    TransferTableProps,
  } from './TransferTableModal.config';
  import { devicePage } from '/@/api/device/deviceManager';
  import { DeviceModel as RawDeviceModal } from '/@/api/device/model/deviceModel';
  import { BasicModal, useModal } from '/@/components/Modal';
  import { BasicTable, useTable } from '/@/components/Table';
  import { FETCH_SETTING } from '/@/components/Table/src/const';
  import { useDesign } from '/@/hooks/web/useDesign';
  import { isFunction } from '/@/utils/is';

  interface DeviceModel extends RawDeviceModal {
    disabled?: boolean;
  }

  const props = withDefaults(
    defineProps<{
      getPendingTableParams: (params: Recordable) => any;
      getSelectedTableParams: (params: Recordable) => any;
      value?: (Recordable & DeviceModel)[];
      maxTagLength?: number;
      openModalValidate?: () => boolean;
      primaryKey?: string;
      transformValue?: (list: Recordable[]) => any;
      disabled?: boolean;
    }>(),
    {
      value: () => [],
      maxTagLength: 2,
      primaryKey: 'tbDeviceId',
    }
  );

  const emit = defineEmits(['update:value']);

  enum Active {
    PENDING = 'pending',
    SELECTED = 'selected',
  }

  const activeKey = ref(Active.PENDING);

  const { prefixCls } = useDesign('transfer-table-modal');

  const pendingTotalList = ref<DeviceModel[]>([]);

  const pendingConfirmQueue = ref<DeviceModel[]>([]);

  const selectedTotalList = ref<DeviceModel[]>([]);

  const selectedConfirmQueue = ref<DeviceModel[]>([]);

  const getShowTagOptions = computed(() => {
    const { maxTagLength } = props;
    return unref(selectedTotalList).slice(0, maxTagLength);
  });

  const getSurplusOptionsLength = computed(() => {
    const { maxTagLength } = props;
    const surplusValue = unref(selectedTotalList).length - maxTagLength;
    return surplusValue < 0 ? 0 : surplusValue;
  });

  // const pendingListCount = computed(() => {
  //   const { value } = props;
  //   const selectedList = unref(pendingTotalList).filter((item) => value.includes(item.id));
  //   return unref(pendingTotalList).length - selectedList.length;
  // });

  const [registerModal, { openModal }] = useModal();

  const [regsterPendingTable, pendingTableActionType] = useTable({
    ...TransferTableProps,
    rowKey: props.primaryKey,
    api: devicePage,
    immediate: false,
    clickToRowSelect: false,
    beforeFetch: (params) => {
      const { getPendingTableParams } = props;
      const data = getPendingTableParams?.(params) || {};
      Object.assign(params, { ...data, selected: false });
      return params;
    },
    afterFetch: (list: DeviceModel[]) => {
      pendingTotalList.value = list;
      return unref(pendingTotalList);
    },
    pagination: { hideOnSinglePage: false },
    rowSelection: {
      type: 'checkbox',
      getCheckboxProps: (record: DeviceModel) => {
        const { primaryKey } = props;
        const checked = unref(selectedTotalList).map((item) => item[primaryKey]);
        return {
          disabled: checked.includes(record[props.primaryKey]!),
        };
      },
      onSelect: (_record: Recordable, _selected: boolean, selectedRows: DeviceModel[]) => {
        const { primaryKey } = props;
        const checked = unref(selectedTotalList).map((item) => item[primaryKey]);
        pendingConfirmQueue.value = selectedRows.filter(
          (item) => !checked.includes(item[primaryKey]!)
        );
      },
      onSelectAll: (_selected: boolean, selectedRows: DeviceModel[]) => {
        const { primaryKey } = props;
        const checked = unref(selectedTotalList).map((item) => item[primaryKey]);
        pendingConfirmQueue.value = selectedRows.filter(
          (item) => !checked.includes(item[primaryKey]!)
        );
      },
    },
  });

  const [registerSelectedTable, selectedTableActionType] = useTable({
    formConfig: {
      layout: 'inline',
      labelWidth: 80,
      schemas: deviceTableFormSchema,
      actionColOptions: { span: 6 },
    },
    size: 'small',
    maxHeight: 240,
    useSearchForm: true,
    columns: deviceTableColumn,
    api: async (params) => {
      const { name = '', deviceType = '' } = params || {};
      const items = unref(selectedTotalList).filter((item) => {
        return (
          (item.name.toUpperCase().includes(name.toUpperCase()) ||
            item.alias?.toUpperCase().includes(name.toUpperCase())) &&
          item.deviceType.toUpperCase().includes(deviceType.toUpperCase())
        );
      });
      return {
        items,
        total: items.length,
      };
    },
    showIndexColumn: false,
    pagination: { hideOnSinglePage: false },
    fetchSetting: FETCH_SETTING,
    rowKey: props.primaryKey,
    dataSource: selectedTotalList,
    clickToRowSelect: false,
    beforeFetch: (params) => {
      const { getSelectedTableParams } = props;
      const data = getSelectedTableParams?.(params) || {};
      Object.assign(params, { ...data, selected: false });
      return params;
    },
    rowSelection: {
      type: 'checkbox',
      onSelect: (_record: Recordable, _selected: boolean, selectedRows: DeviceModel[]) => {
        selectedConfirmQueue.value = selectedRows;
      },
      onSelectAll: (_selected: boolean, selectedRows: DeviceModel[]) => {
        selectedConfirmQueue.value = selectedRows;
      },
    },
  });

  const handleTriggerUpdateValue = () => {
    let list: Recordable[] = cloneDeep(toRaw(unref(selectedTotalList)));
    const { transformValue } = props;
    if (transformValue && isFunction(transformValue)) list = transformValue(list);

    emit('update:value', list);
  };

  const handleSelected = () => {
    const { primaryKey } = props;
    const _list = [...unref(selectedTotalList), ...unref(pendingConfirmQueue)];
    selectedTotalList.value = uniqBy(_list, primaryKey);
    pendingConfirmQueue.value = [];

    handleTriggerUpdateValue();
    pendingTableActionType.setTableData([]);
    nextTick(() => pendingTableActionType.setTableData(unref(pendingTotalList)));
  };

  const handleRemoveSelected = () => {
    const { primaryKey } = props;
    const selectedIds = unref(selectedConfirmQueue).map((selected) => selected[primaryKey]);
    remove(unref(selectedTotalList), (item) => {
      const flag = selectedIds.includes(item[primaryKey]);
      if (flag) {
        pendingTableActionType.deleteSelectRowByKey(item[primaryKey]);
      }
      return flag;
    });

    handleTriggerUpdateValue();

    selectedTableActionType.clearSelectedRowKeys();
    selectedConfirmQueue.value = [];
    selectedTableActionType.reload();
  };

  const handleCheckoutPanel = async (key: Active) => {
    if (key === Active.PENDING) {
      pendingTableActionType.setTableData([]);
      await nextTick();
      pendingTableActionType.setTableData(unref(pendingTotalList));
    } else {
      await nextTick();
      selectedTableActionType.reload();
    }
  };

  const handleOpenModal = async () => {
    const { openModalValidate } = props;

    if (openModalValidate && isFunction(openModalValidate) && !openModalValidate()) return;

    openModal(true);
    await nextTick();
    pendingTableActionType.reload();
  };

  onMounted(async () => {
    const { getSelectedTableParams } = props;
    const data = getSelectedTableParams?.({}) || {};
    if (!data?.convertConfigId || !data?.deviceProfileIds) {
      return;
    }
    const { items } = await devicePage({ page: 1, pageSize: 10, ...data, selected: true });
    selectedTotalList.value = items;
  });
</script>

<template>
  <section>
    <BasicModal
      @register="registerModal"
      title="穿梭表格"
      width="60%"
      :wrapClassName="prefixCls"
      :showOkBtn="false"
      cancelText="关闭"
    >
      <section class="bg-gray-100">
        <Tabs v-model:active-key="activeKey" type="card" @change="handleCheckoutPanel">
          <Tabs.TabPane :key="Active.PENDING">
            <template #tab>
              <div class="flex items-center justify-center">
                <span>待选设备</span>
                <!-- <Badge show-zero :count="pendingListCount" /> -->
              </div>
            </template>
            <BasicTable @register="regsterPendingTable">
              <template #toolbar>
                <section class="flex w-full justify-end items-center">
                  <!-- <Button type="primary">全选</Button> -->
                  <div class="text-blue-400">
                    <span class="mr-2">选择设备:</span>
                    <span>{{ pendingConfirmQueue.length }}</span>
                  </div>
                </section>
              </template>
            </BasicTable>
            <section class="flex justify-end px-4 pb-4">
              <Button
                type="primary"
                @click="handleSelected"
                :disabled="!pendingConfirmQueue.length"
              >
                <span>确定已选</span>
              </Button>
            </section>
          </Tabs.TabPane>
          <Tabs.TabPane :key="Active.SELECTED">
            <template #tab>
              <div class="flex items-center justify-center">
                <span>已选设备</span>
                <!-- <Badge show-zero :count="selectedTotalList.length" /> -->
              </div>
            </template>
            <BasicTable @register="registerSelectedTable">
              <template #toolbar>
                <section class="flex w-full justify-end items-center">
                  <div class="text-blue-400">
                    <span class="mr-2">选择设备:</span>
                    <span>{{ selectedConfirmQueue.length }}</span>
                  </div>
                </section>
              </template>
            </BasicTable>
            <section class="flex justify-end px-4 pb-4">
              <Button
                type="primary"
                :disabled="!selectedConfirmQueue.length"
                @click="handleRemoveSelected"
              >
                <span>移除已选</span>
              </Button>
            </section>
          </Tabs.TabPane>
        </Tabs>
      </section>
    </BasicModal>
    <Button @click="handleOpenModal" type="link" :disabled="disabled">
      <span v-if="!selectedTotalList.length">选择设备</span>
      <div v-if="selectedTotalList.length">
        <Tag
          class="!px-2 !py-1 !bg-gray-50 !border-gray-100"
          v-for="item in getShowTagOptions"
          :key="item[primaryKey]"
        >
          <span>
            {{ item.alias || item.name }}
          </span>
        </Tag>
        <Tag class="!px-2 !py-1 !bg-gray-50 !border-gray-100" v-if="getSurplusOptionsLength">
          <span> +{{ getSurplusOptionsLength }}... </span>
        </Tag>
      </div>
    </Button>
  </section>
</template>

<style lang="less">
  @prefix-cls: ~'@{namespace}-transfer-table-modal';

  .@{prefix-cls} {
    .vben-basic-table {
      padding-top: 0;
    }

    .vben-basic-form > .ant-row {
      width: 100%;
    }

    .ant-tabs-top-bar {
      background-color: #fff;
    }

    .ant-table-placeholder {
      height: auto !important;
    }
  }
</style>