表格支持拖拽

63 阅读4分钟
  1. 给拖拽表格添加属性

image.png

  1. onMounted生命周期执行该方法
const rowDrag = function () {
  // 要拖拽元素的父容器
  const tbody = document.querySelector(
    ".draggable .el-table__body-wrapper tbody"
  );
  if (!tbody) return;
  Sortable.create(tbody as HTMLElement, {
    //  可被拖拽的子元素
    draggable: ".draggable .el-table__row",
    onEnd(event: SortableEvent) {
      console.log("event", event);
      if (event.oldIndex !== undefined && event.newIndex !== undefined) {
        const currRow = tableData.value.splice(event.oldIndex, 1)[0];
        tableData.value.splice(event.newIndex, 0, currRow);
      }
    },
  });
};
  1. 引入文件 import { default as Sortable, SortableEvent } from "sortablejs";

完整代码:

<template>
  <div class="warp-card">
    <div class="breadcrumb">
      <el-breadcrumb separator="/">
        <el-breadcrumb-item>
          <a type="primary" href="/homepage">Home</a>
        </el-breadcrumb-item>
        <el-breadcrumb-item>Issue Details</el-breadcrumb-item>
      </el-breadcrumb>
    </div>
    <div class="form-content" ref="headerRef" id="queryForm">
      <!-- 按钮操作区 -->
      <div
        style="text-align: right; margin-bottom: 10px"
        v-if="route.query.record"
      >
        <el-button
          type="primary"
          @click="recordDeleteAction(recordData?.id)"
          title="Delete"
        >
          <el-icon>
            <Delete />
          </el-icon>
          <span style="margin-left: 2px">Delete</span>
        </el-button>

        <!-- <el-button
          type="primary"
          @click="goAction(recordData?.id)"
          title="Edit"
        >
          <el-icon>
            <Edit />
          </el-icon>
          <span style="margin-left: 2px">Edit</span>
        </el-button> -->

        <template v-if="isPublish">
          <el-button
            type="primary"
            @click="changeIssueStatus(recordData?.id)"
            title="Open"
          >
            <el-icon>
              <Open />
            </el-icon>
            <span style="margin-left: 2px">IsIssue Published Toggle</span>
          </el-button>
        </template>
        <template v-else>
          <el-button
            type="primary"
            @click="changeIssueStatus(recordData?.id)"
            title="TurnOff"
          >
            <el-icon>
              <TurnOff />
            </el-icon>
            <span style="margin-left: 2px">IsIssue Published Toggle</span>
          </el-button>
        </template>
      </div>

      <el-form
        ref="ruleFormRef"
        :model="ruleForm"
        :rules="rules"
        label-width="350px"
        class="demo-ruleForm"
        :size="formSize"
        status-icon
      >
        <div class="form-main">
          <div class="card-box">
            <div class="card-title">
              <h3>Event Details({{ detailTitle }})</h3>
            </div>
            <div class="form-item">
              <el-row class="table-tools">
                <el-col :span="24" style="text-align: right">
                  <el-button
                    type="primary"
                    @click="scheduleBtn"
                    v-if="showSendIssue"
                    >Schedule Paper to This Issue</el-button
                  >

                  <template v-if="tableData.length > 0">
                    <el-button
                      @click="issueBtn"
                      type="primary"
                      v-if="showSendIssue"
                      >Send to Production</el-button
                    >

                    <el-button type="primary" @click="downLoad()">
                      <el-icon>
                        <Download />
                      </el-icon>
                    </el-button>
                    <el-button
                      @click="saveTable()"
                      type="primary"
                      v-if="showSaveTable"
                      >Save Table</el-button
                    >
                  </template>
                  <el-button @click="goBack()"
                    ><el-icon><ArrowLeft /></el-icon>Back</el-button
                  >
                </el-col>
              </el-row>
              <el-table
                class="draggable"
                id="myTable"
                ref="dragTable"
                border
                row-key="id"
                :data="tableData"
                style="width: 100%; margin-top: 10px"
                v-draggable="draggableOptions"
              >
                <el-table-column prop="aid" label="ID" width="180">
                  <template #default="scope">
                    <div style="text-align: left">
                      <el-text
                        style="cursor: pointer"
                        type="primary"
                        @click="goPage(scope.row)"
                      >
                        <svg-icon icon-class="export" />
                        {{ scope.row.aid }}
                      </el-text>
                    </div>
                  </template>
                </el-table-column>
                <el-table-column prop="articleType" label="Type" width="180">
                  <template #default="scope">
                    <span class="ellipsis-line">{{
                      scope.row.article.articleType
                    }}</span>
                  </template>
                </el-table-column>
                <el-table-column prop="title" label="Title" min-width="180">
                  <template #default="scope">
                    <span class="ellipsis-line">{{
                      scope.row.article.title
                    }}</span>
                  </template>
                </el-table-column>
                <el-table-column prop="section" label="Section" min-width="180">
                  <template #default="scope">
                    <span>{{ scope.row.section.title }}</span>
                  </template>
                </el-table-column>

                <el-table-column
                  prop="articleContributorCountry"
                  label="CA Country"
                  width="160"
                >
                  <template #default="scope">
                    <span class="ellipsis-line">{{
                      scope.row.article.articleContributorCountry
                    }}</span>
                  </template>
                </el-table-column>

                <el-table-column prop="online" label="Online" width="80">
                  <template #default="scope">
                    <span>{{ scope.row.online ? "Y" : "N" }}</span>
                  </template>
                </el-table-column>
                <el-table-column prop="state" label="Published?" width="100">
                  <template #default="scope">
                    <span>{{ scope.row.state ? "Y" : "N" }}</span>
                  </template>
                </el-table-column>
                <el-table-column label="GA" width="60">
                  <template #default="scope">
                    <span>{{ scope.row.graphicalAbstract ? "Y" : "N" }}</span>
                  </template>
                </el-table-column>
                <el-table-column prop="supplementaryFile" label="SF" width="60">
                  <template #default="scope">
                    <span>{{ scope.row.supplementaryFile ? "Y" : "N" }}</span>
                  </template>
                </el-table-column>
                <el-table-column label="Action">
                  <template #default="scope">
                    <el-button type="text" @click="deleteAction(scope.row.aid)">
                      <el-icon><Delete /></el-icon>
                    </el-button>
                  </template>
                </el-table-column>
              </el-table>
            </div>
          </div>
        </div>
      </el-form>

      <!-- 父组件把数据给子组件 -->
      <issuePie :pieTableDatas="pieTableDatas"></issuePie>
    </div>
  </div>

  <div class="schedule-modal">
    <el-dialog
      v-model="dialogVisible"
      title="Schedule Paper to This Issue"
      width="80%"
    >
      <template #default>
        <el-form :inline="true" :model="formInline" class="demo-form-inline">
          <el-form-item label="ID: " prop="id">
            <el-input
              placeholder="Please input"
              v-model="formInline.aid"
              @keydown.enter="onSearch"
            />
          </el-form-item>

          <el-form-item>
            <el-button @click="onSearch" type="primary">Search</el-button>
            <el-button @click="onClear">Clear</el-button>
          </el-form-item>
        </el-form>
        <el-button @click="scheduleToIssue" type="primary"
          >Schedule to Issue</el-button
        >
        <div class="selected">
          <img src="~@/assets/i.png" alt="" />
          Selected {{ (multipleSelection && multipleSelection.length) || 0 }}
        </div>
        <el-table
          v-loading="loading"
          ref="multipleTableRef"
          :data="scheduleTableData"
          style="width: 100%"
          @selection-change="handleSelectionChange"
          :header-cell-style="headerCellStyle"
          border
          stripe
        >
          <el-table-column type="selection" width="40" />
          <el-table-column label="ID" prop="id" width="90">
            <template #default="scope">
              <div style="text-align: left">
                <el-text
                  style="cursor: pointer"
                  type="primary"
                  @click="goPage(scope.row)"
                >
                  <svg-icon icon-class="export" />
                  {{ scope.row.aid }}
                </el-text>
              </div>
            </template>
          </el-table-column>
          <el-table-column
            label="Title"
            prop="article_title"
            min-width="300"
            show-overflow-tooltip
          >
          </el-table-column>
          <el-table-column label="Status" prop="status" width="210">
            <template #default="scope">
              <span v-html="scope.row.state"></span>
            </template>
          </el-table-column>
          <el-table-column label="Journal" prop="journal_title" width="80">
          </el-table-column>
          <el-table-column label="Article Type" prop="articleType" width="110">
          </el-table-column>
          <el-table-column
            label="Section"
            width="75"
            prop="section_title"
            show-overflow-tooltip
          >
          </el-table-column>

          <el-table-column
            label="Name"
            prop="name"
            width="150"
            show-overflow-tooltip
          >
            <template #default="scope">
              <div v-for="item in scope.row.contributorNames">
                <div
                  :title="item"
                  style="
                    display: flex;
                    white-space: nowrap;
                    overflow: hidden;
                    text-overflow: ellipsis;
                  "
                >
                  {{ item }}
                </div>
              </div>
            </template>
          </el-table-column>
          <el-table-column label="Email" prop="state" width="90">
            <template #default="scope">
              <div
                v-for="(items, index) in scope.row.contributorEmails"
                style="text-align: left"
              >
                <div class="overhiddle" :title="items">
                  {{ items }}
                </div>
              </div>
            </template>
          </el-table-column>
          <el-table-column label="Affiliation" prop="city" width="90">
            <template #default="scope">
              <div v-if="scope.row.contributorAffiliatoins">
                <div
                  v-for="(items, index) in scope.row.contributorAffiliatoins"
                  style="text-align: left"
                >
                  <div class="overhiddle" :title="items">
                    {{ items }}
                  </div>
                </div>
              </div>
            </template>
          </el-table-column>
        </el-table>
        <div style="text-align: right">
          <pagination
            @pagination="handleQuery"
            v-if="total > 0"
            v-model:limit="queryParams.pageSize"
            v-model:page="queryParams.pageNum"
            v-model:total="total"
          />
        </div>

        <el-dialog
          v-model="innerVisible"
          width="50%"
          title="Confirm Information"
          append-to-body
        >
          <div
            v-if="
              checkArticleDatas.repeatAuthorTips &&
              checkArticleDatas.repeatAuthorTips.length
            "
          >
            <h3>Repeat Author Tips:</h3>
            <div v-for="item in checkArticleDatas.repeatAuthorTips">
              {{ item }}
            </div>
          </div>
          <div
            v-if="
              checkArticleDatas.repeatInstitutionTips &&
              checkArticleDatas.repeatInstitutionTips.length
            "
          >
            <h3>Repeat Institution Tips:</h3>
            <div v-for="item in checkArticleDatas.repeatInstitutionTips">
              {{ item }}
            </div>
          </div>
          <template #footer>
            <div class="dialog-footer">
              <el-button @click="innerVisible = false">Cancel</el-button>
              <el-button type="primary" @click="confirmInfos">
                Confirm
              </el-button>
            </div>
          </template></el-dialog
        >
      </template>
    </el-dialog>
  </div>
</template>

<script lang="ts" setup>
import { onMounted, reactive, ref, nextTick } from "vue";
import type { FormInstance, FormRules } from "element-plus";
import { default as Sortable, SortableEvent } from "sortablejs";
import { useRouter, useRoute } from "vue-router";
import SvgIcon from "@/components/SvgIcon/index.vue";
import issuePie from "@/views/editorialReports/components/issuePie.vue";
import {
  getTable,
  addTable,
  deleteTable,
  deleteIssue,
  send,
  getArticleList,
  checkArticleSameAuthorAndAffiliation,
  saveBatch,
  setIssueStatus,
} from "@/api/issueManagment";
import {
  ElMessage,
  ElMessageBox,
  UploadRequestOptions,
  UploadUserFile,
} from "element-plus";
import { uploadFileApi } from "@/api/review/review";
import { downloadExcelFile } from "@/utils/table";
import Pagination from "@/components/Pagination/index.vue";

const router = useRouter();
const route = useRoute();
const sortList = ref([]);
const pageId = route.query.id;
const detailTitle = route.query.title;
const journal = route.query.jjd;
const fileList = ref<UploadUserFile[]>([]);
let tableData = ref([]);
let showSaveTable = ref(false);
let showSendIssue = ref(false);
// 行拖拽
const rowDrag = function () {
  // 要拖拽元素的父容器
  const tbody = document.querySelector(
    ".draggable .el-table__body-wrapper tbody"
  );
  if (!tbody) return;
  Sortable.create(tbody as HTMLElement, {
    //  可被拖拽的子元素
    draggable: ".draggable .el-table__row",
    onEnd(event: SortableEvent) {
      console.log("event", event);
      if (event.oldIndex !== undefined && event.newIndex !== undefined) {
        const currRow = tableData.value.splice(event.oldIndex, 1)[0];
        tableData.value.splice(event.newIndex, 0, currRow);
      }
    },
  });
};

let recordData: any = reactive({});

/**
 *调用接口
 */

const isPublish = ref(false);

let pieTableDatas = ref(null); // 定义变量

onMounted(() => {
  recordData = route.query.record
    ? JSON.parse(route.query.record)
    : {
        publish: false,
      };
  isPublish.value = recordData?.publish;

  getTableDatas();
  rowDrag();
});
const getTableDatas = async () => {
  getTable({ id: pageId }).then((res) => {
    pieTableDatas.value = res.data; //接口数据放到变量里

    tableData.value = res.data.issueArticleNumberList;
    showSaveTable.value = res.data.showSaveTable;
    showSendIssue.value = res.data.showSendIssue;
  });
};
const saveTable = () => {
  let ids = [];
  tableData.value.map(function (items) {
    ids.push(items.aid);
  });
  addTable(ids).then((res) => {
    ElMessage({
      message: "save successful.",
      type: "success",
    });
  });
};
/**
 *删除
 */
const deleteAction = (id) => {
  ElMessageBox.confirm("Are you sure to delete? ", "Confirmation", {
    confirmButtonText: "Confirm",
    cancelButtonText: "Cancel",
    type: "warning",
  })
    .then(() => {
      deleteTable({ aid: id }).then((res) => {
        getTableDatas();
        ElMessage({
          type: "success",
          message: "Delete completed",
        });
      });
    })
    .catch(() => {
      ElMessage({
        type: "info",
        message: "Delete canceled",
      });
    });
};
/**
 *下载
 */
// Example usage:
const downLoad = () => {
  const table = document.getElementById("myTable");
  downloadExcelFile(table, "myTable.xlsx");
};
const goBack = () => {
  router.go(-1);
};

const issueBtn = () => {
  send({
    issueId: pageId,
  }).then((res) => {
    if (res.code == 2000) {
      ElMessage({
        type: "success",
        message: res.message,
      });
    }
  });
};

const dialogVisible = ref(false);
const scheduleTableData = ref([]);
const scheduleBtn = () => {
  dialogVisible.value = true;
  getPapersList();
};
const getPapersList = () => {
  loading.value = true;
  getArticleList({
    pageSize: queryParams.pageSize,
    pageNumber: queryParams.pageNum,
    issueId: pageId,
    aid: formInline.aid,
  }).then((res) => {
    loading.value = false;
    scheduleTableData.value = res.data.content;
    total.value = res.data.total;
  });
};

const multipleTableRef = ref();
const multipleSelection = ref([]);

const handleSelectionChange = (val) => {
  multipleSelection.value = val;
};

const formInline = reactive({
  aid: "",
});
const handleQuery = () => {
  getPapersList();
};
const total = ref(0);
const queryParams = reactive({
  pageNum: 1,
  pageSize: 10,
});

const loading = ref(false);

const onSearch = () => {
  queryParams.pageNum = 1;
  queryParams.pageSize = 10;
  getPapersList();
};

const onClear = () => {
  formInline.aid = "";
  queryParams.pageNum = 1;
  queryParams.pageSize = 10;
  getPapersList();
};

const checkArticleDatas = reactive({
  repeatAuthorTips: [],
  repeatInstitutionTips: [],
  repeatResult: false,
});

const scheduleToIssue = () => {
  let aidList: any = [];

  if (multipleSelection.value && multipleSelection.value.length == 0) {
    return ElMessage({
      type: "error",
      message: "Please select at least one piece of data",
    });
  }

  multipleSelection.value.forEach((item) => {
    aidList.push(item.aid);
  });

  console.log("外面的 tableData", tableData.value);
  console.log("里面的 选中的", multipleSelection.value);

  // ---------------整理数据
  let newArr: any = [];
  tableData.value.forEach((item: any) => {
    newArr.push({
      aid: item.aid,
      articleType: item.article.articleType,
    });
  });

  multipleSelection.value.forEach((item: any) => {
    newArr.push({
      aid: item.aid,
      articleType: item.articleType,
    });
  });

  // ---------------整理数据

  if (checkReviewCount(newArr)) {
    // 点击Schedule To Issue 校验所选内容articletype为Review的是否超过50%,
    // 超过,关闭已开启弹窗,并打开二次弹窗提醒“
    dialogVisible.value = false;
    ElMessageBox.confirm(
      "The selected content has been reviewed more than 50%. Please confirm and continue with the schedule.",
      "Confirmation",
      {
        confirmButtonText: "Confirm",
        cancelButtonText: "Cancel",
        type: "warning",
      }
    )
      .then(() => {
        dialogVisible.value = false;
        checkArticleSameAuthorAndAffiliation({
          aidList: aidList,
          IssueId: pageId,
        }).then((res) => {
          checkArticleDatas.repeatAuthorTips = res.data.repeatAuthorTips;
          checkArticleDatas.repeatInstitutionTips =
            res.data.repeatInstitutionTips;
          checkArticleDatas.repeatResult = res.data.repeatResult;
          getTableDatas();
          if (checkArticleDatas.repeatResult) {
            innerVisible.value = true;
          } else {
            confirmInfos();
          }
        });
      })
      .catch(() => {
        ElMessage({
          type: "info",
          message: "canceled",
        });

        dialogVisible.value = true;
      });
  } else {
    // 未超过,添加成功提示,
    checkArticleSameAuthorAndAffiliation({
      aidList: aidList,
      IssueId: pageId,
    }).then((res) => {
      checkArticleDatas.repeatAuthorTips = res.data.repeatAuthorTips;
      checkArticleDatas.repeatInstitutionTips = res.data.repeatInstitutionTips;
      checkArticleDatas.repeatResult = res.data.repeatResult;
      getTableDatas();
      if (checkArticleDatas.repeatResult) {
        innerVisible.value = true;
      } else {
        confirmInfos();
      }
    });
  }
};

// 校验所选内容articletype为Review的是否超过50%
const checkReviewCount = (data) => {
  const total = data.length;
  const reviewCount = data.filter(
    (item) => item.articleType == "Review"
  ).length;
  return reviewCount > total / 2;
};

const goPage = (datas) => {
  let routeData = router.resolve({
    path: "/papersMain/page",
    query: { id: datas.aid },
  });
  window.open(routeData.href, "_blank");
};

const headerCellStyle = () => {
  return {
    backgroundColor: "#f5f7fa",
  };
};
const innerVisible = ref(false);

const confirmInfos = () => {
  let articleListParams: any = [];
  multipleSelection.value.forEach((item) => {
    articleListParams.push({
      aid: item.aid,
      issueId: pageId,
      title: item.article_title,
      authors: item.contributorNames.join(","),
    });
  });

  saveBatch({
    articleListParams: articleListParams,
  }).then((res) => {
    if (res.code == 2000) {
      ElMessage({
        type: "success",
        message: res.message,
      });
      getTableDatas();
    } else {
      ElMessage({
        type: "error",
        message: res.message,
      });
    }
    innerVisible.value = false;
    dialogVisible.value = false;
  });
};

const recordDeleteAction = (id) => {
  ElMessageBox.confirm("Are you sure to delete? ", "Confirmation", {
    confirmButtonText: "Confirm",
    cancelButtonText: "Cancel",
    type: "warning",
  })
    .then(() => {
      deleteIssue({ issueId: id }).then((res) => {
        ElMessage({
          type: "success",
          message: "Delete completed",
        });
        goBack();
      });
    })
    .catch(() => {
      ElMessage({
        type: "info",
        message: "Delete canceled",
      });
    });
};

const goAction = async (id) => {
  router.push({
    path: "issueAction",
    query: {
      id: id ? id : "",
    },
  });
};

const changeIssueStatus = (id) => {
  setIssueStatus({ issueId: id }).then((res) => {
    isPublish.value = !isPublish.value;
    ElMessage({
      type: "success",
      message: "success",
    });
  });
};
</script>

<style scoped>
.dialog-footer button:first-child {
  margin-right: 10px;
}
.demo-form-inline .el-input {
  --el-input-width: 220px;
}

.selected {
  display: flex;
  align-items: center;
  padding-left: 8px;
  width: 100%;
  height: 30px;
  background-color: #e6f7ff;
  border: 1px solid #c8ecff;
  margin-top: 10px;
  border-radius: 3px;
  font-weight: 500;
  margin-bottom: 10px;
  img {
    margin-right: 5px;
  }
}
.cardBox {
  .list {
    max-height: 75px;
    overflow: hidden;
  }
  .down {
    display: block;
  }
  .up {
    display: none;
  }

  &.authHeight {
    .list {
      height: auto;
      max-height: inherit;
    }
    .down {
      display: none;
    }
    .up {
      display: block;
    }
  }
}
</style>