带调换位置的穿梭框(非组件好按需求修改)

 标签部分

<div class="search-box">
                <div>
                  <div
                    style="
                      width: 100%;
                      text-align: center;
                      font-size: 16px;
                      font-weight: 700;
                      margin-bottom: 10px;
                    "
                  >
                    可用字段
                  </div>
                  <div class="one-box">
                    <div
                      v-for="(item, index) in sortleftDatas"
                      :key="index"
                      :class="{ hovered: hoverIndex === index }"
                      @mouseenter="onMouseEnter(index)"
                      @mouseleave="onMouseLeave"
                      style="
                        padding: 10px 20px;
                        font-size: 14px;
                        display: flex;
                        align-items: center;
                      "
                      @click="checkWords(item)"
                    >
                      <div
                        v-if="!item.checked"
                        class="check-box"
                        :class="{ 'check-box1': hoverIndex === index }"
                      ></div>
                      <div v-if="item.checked" class="check-box2"><check-outlined /></div>
                      <div>{{ item.name }}</div>
                    </div>
                  </div>
                </div>
                <div class="box-btn">
                  <div>
                    <div style="margin-bottom: 6px">添加</div>
                    <a-button type="primary" @click="addRight">
                      <template #icon>
                        <CaretRightOutlined style="font-size: 22px" />
                      </template>
                    </a-button>
                  </div>
                  <div style="margin-top: 20px">
                    <a-button type="primary" @click="addLeft">
                      <template #icon>
                        <CaretLeftOutlined style="font-size: 22px" />
                      </template>
                    </a-button>
                    <div style="margin-top: 6px">移除</div>
                  </div>
                </div>
                <div>
                  <div
                    style="
                      width: 100%;
                      text-align: center;
                      font-size: 16px;
                      font-weight: 700;
                      margin-bottom: 10px;
                    "
                  >
                    选取的字段
                  </div>
                  <div class="one-box">
                    <div
                      v-for="(item, index) in sortrightDatas"
                      :key="index"
                      :class="{ hovered: hoverIndex2 === index }"
                      @mouseenter="onMouseEnter2(index)"
                      @mouseleave="onMouseLeave2"
                      style="
                        padding: 10px 20px;
                        font-size: 14px;
                        display: flex;
                        align-items: center;
                      "
                      @click="checkWords(item)"
                    >
                      <div
                        v-if="!item.checked"
                        class="check-box"
                        :class="{ 'check-box1': hoverIndex2 === index }"
                      ></div>
                      <div v-if="item.checked" class="check-box2"><check-outlined /></div>
                      <div>{{ item.name }}</div>
                    </div>
                  </div>
                </div>
                <div class="box-btn">
                  <div>
                    <div style="margin-bottom: 6px">向上</div>
                    <a-button type="primary" @click="upNum">
                      <template #icon>
                        <CaretUpOutlined style="font-size: 22px" />
                      </template>
                    </a-button>
                  </div>
                  <div style="margin-top: 20px">
                    <a-button type="primary" @click="downNum">
                      <template #icon>
                        <CaretDownOutlined style="font-size: 22px" />
                      </template>
                    </a-button>
                    <div style="margin-top: 6px">向下</div>
                  </div>
                </div>
              </div>

css样式部分

.search-box {
    display: flex;
    margin: 40px 0 20px;

    .one-box {
      width: 340px;
      height: 420px;
      overflow-y: scroll;
      border: 1px solid #c3c0c0ec;
      display: flex;
      flex-direction: column;
      padding-top: 10px;

      .hovered {
        background-color: #f0f0f0;
        cursor: pointer;
      }

      .check-box {
        width: 20px;
        height: 20px;
        border: 1px solid #d4d4d4;
        border-radius: 2px;
        margin-right: 12px;
      }

      .check-box1 {
        width: 20px;
        height: 20px;
        border: 1px solid #3a91f5;
        border-radius: 2px;
        margin-right: 12px;
      }

      .check-box2 {
        width: 20px;
        height: 20px;
        border: 1px solid #3a91f5;
        color: white;
        background-color: #3a91f5;
        border-radius: 2px;
        margin-right: 12px;
        display: flex;
        justify-content: center;
        align-items: center;
      }
    }

    .box-btn {
      width: 100px;
      display: flex;
      justify-content: center;
      flex-direction: column;
      align-items: center;
    }
  }

 逻辑部分

 const leftOptions = reactive({
    datas: [
      { name: '字段1', checked: false, num: 0 },
      { name: '字段3', checked: false, num: 3 },
      { name: '字段2', checked: false, num: 2 },
      { name: '字段4', checked: false, num: 4 },
      { name: '字段5', checked: false, num: 5 },
      { name: '字段6', checked: false, num: 6 },
      { name: '字段7', checked: false, num: 7 },
      { name: '字段8', checked: false, num: 8 },
    ],
  })
  // 计算属性,返回按 num 排序的数据
  const sortleftDatas = computed(() => {
    return [...leftOptions.datas].sort((a, b) => a.num - b.num)
  })
  const rightOptions = reactive({
    datas: [],
  })
  // 计算属性,返回按 num 排序的数据
  const sortrightDatas = computed(() => {
    return [...rightOptions.datas].sort((a, b) => a.num - b.num)
  })
  const hoverIndex = ref(-1) // 用于跟踪鼠标悬停的索引

  const onMouseEnter = (index) => {
    hoverIndex.value = index
  }

  const onMouseLeave = () => {
    hoverIndex.value = -1
  }
  const hoverIndex2 = ref(-1) // 用于跟踪鼠标悬停的索引

  const onMouseEnter2 = (index) => {
    hoverIndex2.value = index
  }

  const onMouseLeave2 = () => {
    hoverIndex2.value = -1
  }

  //选择选项
  const checkWords = (item) => {
    item.checked = !item.checked
  }

  //添加按钮
  const addRight = () => {
    let leftdata = leftOptions.datas
      .filter((item) => item.checked == true)
      .map((item) => {
        return {
          ...item,
          checked: false,
        }
      })
    leftOptions.datas = leftOptions.datas.filter((item) => item.checked != true)
    rightOptions.datas = [...rightOptions.datas]
    rightOptions.datas.unshift(...leftdata)
    rightOptions.datas = rightOptions.datas.map((item, index) => {
      return {
        ...item,
        num: index,
      }
    })
    // console.log('rightOptions2', rightOptions.datas)
  }

  //移除按钮
  const addLeft = () => {
    let rightdata = rightOptions.datas
      .filter((item) => item.checked == true)
      .map((item) => {
        return {
          ...item,
          checked: false,
        }
      })
    rightOptions.datas = rightOptions.datas.filter((item) => item.checked != true)
    rightOptions.datas = rightOptions.datas.map((item, index) => {
      return {
        ...item,
        num: index,
      }
    })
    leftOptions.datas = [...leftOptions.datas]
    leftOptions.datas.unshift(...rightdata)
    leftOptions.datas = leftOptions.datas.map((item, index) => {
      return {
        ...item,
        num: index,
      }
    })
    // console.log('leftOptions2', leftOptions.datas)
  }
  //向上按钮
  const upNum = () => {
    for (let i = 0; i < rightOptions.datas.length; i++) {
      const currentItem = rightOptions.datas[i]

      if (currentItem.checked === true) {
        let newIndex = i - 1

        while (newIndex >= 0 && rightOptions.datas[newIndex].checked == true) {
          newIndex--
        }

        if (newIndex < 0) {
          newIndex = 0
        }

        rightOptions.datas.splice(newIndex, 0, rightOptions.datas.splice(i, 1)[0])

        rightOptions.datas = rightOptions.datas.map((item, index) => ({
          ...item,
          num: index,
        }))
      }
    }
    // console.log('leftOptions4', rightOptions.datas)
  }
  //向下按钮
  const downNum = () => {
    for (let i = rightOptions.datas.length - 1; i >= 0; i--) {
      const currentItem = rightOptions.datas[i]

      if (currentItem.checked === true) {
        let newIndex = i + 1
        while (
          newIndex < rightOptions.datas.length &&
          rightOptions.datas[newIndex].checked === true
        ) {
          newIndex++
        }

        if (newIndex === rightOptions.datas.length) {
          newIndex = i
        }

        rightOptions.datas.splice(newIndex, 0, rightOptions.datas.splice(i, 1)[0])

        rightOptions.datas = rightOptions.datas.map((item, index) => ({
          ...item,
          num: index,
        }))

        let j = newIndex - 1
        while (j > i && rightOptions.datas[j - 1].checked === false) {
          rightOptions.datas.splice(j, 0, rightOptions.datas.splice(j - 1, 1)[0])
          j--

          rightOptions.datas = rightOptions.datas.map((item, index) => ({
            ...item,
            num: index,
          }))
        }
      }
    }
    // console.log('leftOptions3', rightOptions.datas)
  }

页面效果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值