sortablejs实现列表拖拽排序

2022-07-28 20:39:00
2024-04-22 11:24:49

引用方法

Vue3

npm i -S sortablejs
import Sortable from "sortablejs";

使用方法

<template>
  <el-card class="box-card">
    <template #header>
      <div class="flex justify-between">
        <span>友链列表</span>
        <el-button type="primary" @click="addEditFriendLinks">添加友链</el-button>
      </div>
    </template>

    <div class="table1">
      <el-table
        ref="dragTable"
        :data="tableData"
        style="width: 100%"
        border
        show-overflow-tooltip
        stripe
        class="t1"
        row-key="id"
        :row-class-name="tableRowClassName"
      >
        <el-table-column prop="orderId" label="排序" align="center" width="100" class="sorting">
          <template #default="scope">
            <el-button class="move" type="text" size="small">{{ scope.row.orderId }}</el-button>
          </template>
        </el-table-column>
        <el-table-column prop="avatar" label="友链Logo" align="center" width="100">
          <template #default="scope">
            <a :href="scope.row.url" target="_blank">
              <img :src="scope.row.avatar" class="avatar" />
            </a>
          </template>
        </el-table-column>
        <el-table-column prop="name" label="友链名称" align="center" width="150" />
        <el-table-column
          prop="status"
          :formatter="formatterStatus"
          label="状态"
          width="120"
          align="center"
        />
        <el-table-column prop="desc" :formatter="formatterDesc" label="简介描述" align="center" />
        <el-table-column prop="url" label="友链地址" align="center">
          <template #default="scope">
            <a :href="scope.row.url" target="_blank">{{ scope.row.url }}</a>
          </template>
        </el-table-column>
        <el-table-column prop="createdAt" label="创建时间" align="center" width="120" />
        <el-table-column prop="updatedAt" label="更新时间" align="center" width="120" />
        <el-table-column prop="address" label="操作" align="center" width="220">
          <template #default="scope">
            <el-button @click="handleEdit(scope.row)">编辑</el-button>
            <el-popconfirm
              title="确定删除这个友链么?"
              confirm-button-text="确定"
              cancel-button-text="取消"
              icon="el-icon-info"
              icon-color="red"
              @confirm="deleteType(scope.row.id)"
            >
              <template #reference>
                <el-button type="danger">删除</el-button>
              </template>
            </el-popconfirm>
          </template>
        </el-table-column>
      </el-table>
    </div>
    <!-- dialog -->
    <el-dialog v-model="visible" :title="titleMsg" width="400px">
      <el-form ref="formRef" :model="formData" :rules="rules">
        <el-form-item label="友链名称" prop="name" label-width="90px">
          <el-input v-model="formData.name" placeholder="请填写友链名称" />
        </el-form-item>
        <el-form-item label="友链logo" prop="avatar" label-width="90px">
          <el-input v-model="formData.avatar" placeholder="请填写友链logo图片地址" />
        </el-form-item>
        <el-form-item label="友链地址" prop="url" label-width="90px">
          <el-input v-model="formData.url" placeholder="请填写友链地址链接" />
        </el-form-item>
        <el-form-item label="友链简述" prop="desc" label-width="90px">
          <el-input
            v-model="formData.desc"
            :rows="3"
            type="textarea"
            placeholder="请填写友链简述说明"
          />
        </el-form-item>
        <el-form-item label="友链状态" prop="status" label-width="90px">
          <el-select v-model="formData.status" placeholder="请选择友链状态">
            <el-option
              v-for="item in statusMap"
              :key="item.value"
              :label="item.label"
              :value="item.value"
            />
          </el-select>
        </el-form-item>
        <el-form-item label="排序定义" label-width="90px" prop="orderId">
          <el-input v-model="formData.orderId" placeholder="填写排序数字[不填写系统自动赋予]" />
        </el-form-item>
      </el-form>
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="visible = false">取 消</el-button>
          <el-button type="primary" @click="submit">确 定</el-button>
        </span>
      </template>
    </el-dialog>

    <!-- 分页 -->
    <div class="flex justify-center mt-6">
      <el-pagination
        :current-page="params.page"
        :page-size="params.pageSize"
        :page-sizes="[10, 20, 30, 10000]"
        :disabled="disabled"
        :background="background"
        layout="sizes, prev, pager, next, jumper"
        :total="total"
        @size-change="handleSizeChange"
        @current-change="changePage"
      />
    </div>
  </el-card>
</template>

<script setup lang="ts">
  import {
    getFriendLinks,
    createFriendLinks,
    updateFriendLinks,
    delFriendLinks,
  } from '@/api/friend-links';
  import { ElMessage } from 'element-plus';
  import { formatTime } from '@/utils/tools';
  import { statusMap } from './constant';

  import Sortable from 'sortablejs';
  const background = ref(false);
  const disabled = ref(false);

  const handleSizeChange = (val: number) => {
    params.value.pageSize = val;
    queryFriendLinks();
  };
  const formRef: any = ref(null);
  const data: any = reactive({
    tableData: [],
    total: null,
    params: {
      page: 1,
      pageSize: 10,
    },
    activeId: null,
    formData: {
      name: null,
      status: 1,
      orderId: null,
      desc: null,
      url: null,
      avatar: null,
    },
    visible: false,
    type: 1,
    rules: {
      name: [{ required: true, message: '请填写友链名称', trigger: 'blur' }],
      desc: [{ required: true, message: '请填写友链描述', trigger: 'blur' }],
      url: [{ required: true, message: '请填写友链地址', trigger: 'blur' }],
      avatar: [{ required: true, message: '请填写友链logo地址', trigger: 'blur' }],
      status: [{ required: true, message: '请选择友链状态', trigger: 'blur' }],
    },
  });

  const { tableData, total, params, formData, visible, rules } = toRefs(data);

  /* 查询友链列表 */
  async function queryFriendLinks() {
    const res: any = await getFriendLinks(data.params);
    formatTime(res.rows);
    data.tableData = res.rows;
    data.total = res.count;
  }

  queryFriendLinks();

  /* 添加或者修改友链 */
  function addEditFriendLinks() {
    data.type = 1;
    data.visible = true;
  }

  const submit = async () => {
    formRef.value.validate(async (valid) => {
      if (valid) {
        let param = JSON.parse(JSON.stringify(data.formData));
        param.id && delete param.id;
        data.type === 2 && (param.id = data.activeId);

        if (data.type === 1) {
          await createFriendLinks(param);
        } else {
          await updateFriendLinks(param);
        }

        ElMessage({ message: '操作成功', type: 'success' });
        data.visible = false;
        formRef.value.resetFields();
        data.formData = resetForm();
        queryFriendLinks();
      }
    });
  };

  function changePage(val) {
    data.params.page = val;
    queryFriendLinks();
  }

  function handleEdit(row) {
    const { name, status, orderId, desc, url, avatar, id } = row;
    data.activeId = id;
    data.type = 2;
    Object.assign(data.formData, { name, status, orderId, desc, url, avatar });
    data.visible = true;
  }

  async function deleteType(id) {
    await delFriendLinks({ id });
    ElMessage({ message: '删除成功', type: 'success' });
    queryFriendLinks();
  }

  const titleMsg = computed(() => {
    return data.type === 1 ? '添加友链' : '修改友链';
  });

  const resetForm = () => {
    return { name: null, status: 1, orderId: null };
  };

  function formatterStatus(row) {
    return row.status == 1 ? '激活' : '禁止';
  }

  function formatterDesc(row) {
    return row.desc.length > 16 ? row.desc.substr(0, 16) + '...' : row.desc;
  }

  // 创建sortable实例
  function initSortable(className) {
    // 获取表格row的父节点
    const table = document.querySelector('.' + className + ' .el-table__body-wrapper tbody');
    // 创建拖拽实例
    let dragTable = Sortable.create(table, {
      //动画
      animation: 150,
      // 拖拽不可用? false 启用(刚刚渲染表格的时候起作用,后面不起作用)
      disabled: false,
      //指定拖拽目标,点击此目标才可拖拽元素(此例中设置操作按钮拖拽)
      handle: '.move',
      //指定不可拖动的类名(el-table中可通过row-class-name设置行的class)
      filter: '.disabled',
      //设置拖拽样式类名
      dragClass: 'dragClass',
      //设置拖拽停靠样式类名
      ghostClass: 'ghostClass',
      //设置选中样式类名
      chosenClass: 'chosenClass',
      // 开始拖动事件
      onStart: () => {
        console.log('开始拖动');
      },
      // 结束拖动事件
      onEnd: async ({ newIndex, oldIndex }) => {
        console.log('结束拖动', `拖动前索引${oldIndex}---拖动后索引${newIndex}`);
        const currRow = tableData.value.splice(oldIndex, 1)[0];
        tableData.value.splice(newIndex, 0, currRow);
        console.log('结束拖动', tableData.value);

        let newTableData: any = [];
        //更新排序
        tableData.value.map(async (item, index) => {
          item.orderId = index + 1 + (params.value.page - 1) * params.value.pageSize;
          newTableData.push({
            id: item.id,
            orderId: item.orderId,
          });

          console.log(item.orderId);
        });

        await updateFriendLinks(newTableData);
      },
    });

    console.log('dragTable', dragTable);
  }
  // 设置表格row的class
  function tableRowClassName({ row }) {
    if (row.disabled) {
      return 'disabled';
    }
    return '';
  }
  onMounted(() => {
    initSortable('t1');
  });
</script>

<style lang="scss" scoped>
  .el-select {
    width: 100%;
  }
  .avatar {
    width: 50px;
    height: 50px;
    border-radius: 50%;
    margin: 0 auto;
  }
</style>

常见问题

目录
2222

1112222

回复
0 - 0
Chrome120.0.0.0
Windows10
2023-11-07 09:40:50

运营需要亿点资金维持,您的支持,是小白龙创作的动力!!!

昵称
留言
赞赏金额