目录

sortablejs实现列表拖拽排序

Admin7/28/20221645 阅读

引用方法

Vue3

shell 复制代码
npm i -S sortablejs
shell 复制代码
import Sortable from \"sortablejs\";

使用方法

js 复制代码
<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>

常见问题

评论

共 0 条
后参与评论
共 104 篇文章 感谢支持

RSS ·关于

© 2026 我的博客