index.vue 8.58 KB
<script lang="ts" setup>
  import { List, Card, Statistic, Button, Tooltip, Spin } from 'ant-design-vue';
  import { onMounted, ref, unref } from 'vue';
  import { PageWrapper } from '/@/components/Page';
  import { MoreOutlined, ShareAltOutlined } from '@ant-design/icons-vue';
  import { useCopyToClipboard } from '/@/hooks/web/useCopyToClipboard';
  import { useMessage } from '/@/hooks/web/useMessage';
  import Dropdown from '/@/components/Dropdown/src/Dropdown.vue';
  import { DropMenu } from '/@/components/Dropdown';
  import { DATA_BOARD_SHARE_URL, MoreActionEvent } from './config/config';
  import { useModal } from '/@/components/Modal';
  import PanelDetailModal from './components/PanelDetailModal.vue';
  import { getDataBoardList, deleteDataBoard } from '/@/api/dataBoard';
  import { DataBoardRecord } from '/@/api/dataBoard/model';
  import { ViewType } from './config/panelDetail';
  import { useRouter } from 'vue-router';
  import { getBoundingClientRect } from '/@/utils/domUtils';
  import Authority from '/@/components/Authority/src/Authority.vue';
  import { computed } from '@vue/reactivity';
  import { usePermission } from '/@/hooks/web/usePermission';
  import { encode } from './config/config';

  const ListItem = List.Item;
  const router = useRouter();

  const { createMessage, createConfirm } = useMessage();

  const listEL = ref();
  const loading = ref(false);
  const dataBoardList = ref<DataBoardRecord[]>([]);
  //分页相关
  const page = ref(1);
  const pageSize = ref(10);
  const total = ref(0);
  const paginationProp = ref({
    showSizeChanger: false,
    showQuickJumper: true,
    pageSize,
    current: page,
    total,
    showTotal: (total) => `总 ${total} 条`,
    onChange: pageChange,
    onShowSizeChange: pageSizeChange,
  });

  function pageChange(p, pz) {
    page.value = p;
    pageSize.value = pz;
    getDatasource();
  }
  function pageSizeChange(_current, size) {
    pageSize.value = size;
    getDatasource();
  }

  const createShareUrl = (boardId: string, tenantId: string, name: string) => {
    const { origin } = location;
    return `${origin}${DATA_BOARD_SHARE_URL(encode(boardId), encode(tenantId), encode(name))}`;
  };

  const { clipboardRef } = useCopyToClipboard();
  const handleCopyShareUrl = (record: DataBoardRecord) => {
    clipboardRef.value = createShareUrl(record.id, record.tenantId, record.name);
    unref(clipboardRef) ? createMessage.success('复制成功') : createMessage.error('未找到分享链接');
  };

  const { hasPermission } = usePermission();
  const dropMenuList = computed<DropMenu[]>(() => {
    const hasUpdatePermission = hasPermission('api:yt:data_board:update:update');
    const hasDeletePermission = hasPermission('api:yt:data_board:delete');
    const basicMenu: DropMenu[] = [];
    if (hasUpdatePermission)
      basicMenu.push({
        text: '编辑',
        event: MoreActionEvent.EDIT,
        icon: 'ant-design:edit-outlined',
      });
    if (hasDeletePermission)
      basicMenu.push({
        text: '删除',
        event: MoreActionEvent.DELETE,
        icon: 'ant-design:delete-outlined',
      });
    return basicMenu;
  });

  const getDatasource = async () => {
    try {
      loading.value = true;
      const { total, items } = await getDataBoardList({
        page: unref(paginationProp).current,
        pageSize: unref(paginationProp).pageSize,
      });
      dataBoardList.value = items;
      paginationProp.value.total = total;
    } catch (error) {
    } finally {
      loading.value = false;
    }
  };

  const handleMenuEvent = (event: DropMenu, record: DataBoardRecord) => {
    if (event.event === MoreActionEvent.EDIT) handleEdit(record);
    if (event.event === MoreActionEvent.DELETE) {
      createConfirm({
        iconType: 'warning',
        content: '是否确认删除操作?',
        onOk: () => handleRemove(record),
      });
    }
  };

  const handleEdit = (record: DataBoardRecord) => {
    openModal(true, {
      isEdit: true,
      ...record,
    });
  };

  const handleOpenDetailModal = () => {
    openModal();
  };

  const handleRemove = async (record: DataBoardRecord) => {
    // TODO 删除确认
    try {
      await deleteDataBoard([record.id]);
      createMessage.success('删除成功');
      await getDatasource();
    } catch (error) {
      // createMessage.error('删除失败');
    }
  };

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

  const handleViewBoard = (record: DataBoardRecord) => {
    const hasDetailPermission = hasPermission('api:yt:data_component:list');
    if (hasDetailPermission)
      router.push(`/data/board/detail/${encode(record.id)}/${encode(record.name)}`);
    else createMessage.warning('没有权限');
  };

  const handlePagenationPosition = () => {
    const clientHeight = document.documentElement.clientHeight;
    const rect = getBoundingClientRect(unref(listEL).$el!) as DOMRect;
    const paginationHeight = 32 + 24 + 16;
    const listContainerMarginBottom = 16;
    const listContainerHeight =
      clientHeight - rect.top - paginationHeight - listContainerMarginBottom;
    const listContainerEl = (unref(listEL).$el as HTMLElement).querySelector(
      '.ant-spin-container'
    ) as HTMLElement;
    listContainerEl &&
      // (listContainerEl.style.minHeight = listContainerHeight + 'px') &&
      // (listContainerEl.style.maxHeight = listContainerHeight + 'px') &&
      (listContainerEl.style.height = listContainerHeight + 'px') &&
      (listContainerEl.style.overflowY = 'auto') &&
      (listContainerEl.style.overflowX = 'hidden');
  };

  onMounted(() => {
    getDatasource();
    handlePagenationPosition();
  });
</script>

<template>
  <PageWrapper>
    <div class="flex mb-6 items-center bg-light-100 h-78px">
      <div class="text-lg ml-30px mr-9px font-bold">自定义看板</div>
      <Authority value="api:yt:data_board:add:post">
        <Button type="primary" @click="handleOpenDetailModal">创建看板</Button>
      </Authority>
    </div>
    <Spin :spinning="loading">
      <List
        ref="listEL"
        :pagination="paginationProp"
        :data-source="dataBoardList"
        :grid="{ gutter: 20, column: 4, xs: 1, sm: 2, md: 2, lg: 3, xl: 3, xxl: 3 }"
      >
        <template #renderItem="{ item }">
          <ListItem>
            <Card class="data-card cursor-pointer">
              <template #title>
                <div>{{ item.name }}</div>
              </template>
              <template #extra>
                <Dropdown
                  v-if="dropMenuList.length"
                  :trigger="['click']"
                  @menu-event="(event) => handleMenuEvent(event, item)"
                  :drop-menu-list="dropMenuList"
                >
                  <MoreOutlined class="rotate-90 transform cursor-pointer" />
                </Dropdown>
              </template>
              <!-- <template #cover>title</template> -->
              <section @click="handleViewBoard(item)">
                <div class="flex data-card__info">
                  <div>
                    <div>组件数量</div>
                    <Statistic style="font-size: 22px" :value="item.componentNum">
                      <template #suffix>
                        <span class="text-sm">个</span>
                      </template>
                    </Statistic>
                  </div>
                </div>
                <div class="flex justify-between mt-4 text-sm" style="color: #999999">
                  <div>
                    <span>
                      {{ item.viewType === ViewType.PRIVATE_VIEW ? '私有看板' : '公共看板' }}
                    </span>
                    <!-- <span v-if="item.viewType === ViewType.PUBLIC_VIEW">
                      <Tooltip title="点击复制分享链接">
                        <ShareAltOutlined class="ml-2" @click.stop="handleCopyShareUrl(item)" />
                      </Tooltip>
                    </span> -->
                  </div>
                  <div>{{ item.createTime }}</div>
                </div>
              </section>
            </Card>
          </ListItem>
        </template>
      </List>
    </Spin>
    <PanelDetailModal @register="registerModal" @change="getDatasource" />
  </PageWrapper>
</template>

<style scoped lang="less">
  .data-card:deep(.ant-card-head) {
    padding: 20px;
  }

  .data-card:deep(.ant-card-head-title) {
    padding: 0;
  }
  .data-card:deep(.ant-card-extra) {
    padding: 0;
  }
  .data-card:deep(.ant-card-body) {
    padding: 20px;
  }

  .data-card__info {
    color: #666666;

    &::before {
      content: '';
      width: 54px;
      height: 54px;
      margin-right: 16px;
      background-image: url('/@/assets/svg/component.svg');
    }
  }
</style>