单选 多选混合

支持单选 和多选同时存在

一封装下拉框组件

<template>
  <div ref="dropdownRef" class="drop_down" id="d1" v-click-outside="closeOrder">
    <div
      class="cascade-input"
      @click="toggleDropdown"
      :class="{ 'is-active': isDropdownVisible }"
      @mouseenter="mouseShowDelete"
      @mouseleave="noShowDelete"
    >
      <span class="cascade_choose">{{ inputValue == "" ? "请选择" : inputValue }}</span>
      <el-icon
        :class="{ 'is-rotate': isDropdownVisible }"
        class="down"
        v-if="!(inputValue != '' && isShowDelete)"
        ><ArrowDown
      /></el-icon>
      <el-icon v-if="inputValue != '' && isShowDelete" @click.stop="deleteInput"
        ><CircleClose
      /></el-icon>
    </div>
    <div class="cascade-dropdown" v-if="isDropdownVisible">
      <div style="display: flex">
        <!-- 一级菜单 -->
        <ul>
          <li
            class="cascade_item"
            v-for="(item, index) in cascadeType"
            :key="index"
            @click="chooseType(item.field)"
            :class="{ active: chooseTypeIndex == item.field }"
          >
            <span class="cascade_item_name">{{ item.name }}</span
            ><span class="cascade_item_name">
              <el-icon><ArrowRight /></el-icon>
            </span>
          </li>
        </ul>
        <!-- 二级菜单 -->
        <div class="choose" v-show="isShowTwoMenu">
          <!-- 必选非必选 -->
          <div class="choose_header">
            <ul>
              <li
                style="cursor: pointer"
                v-for="(item, index) in MustList"
                @click="changeMustTab(item.id)"
                :key="index"
                :class="{ 'is-must': isMust == item.id }"
                >{{ item.name }}</li
              >
            </ul>
          </div>
          <div class="choose_content">
            <!-- 系统字段 -->
            <ul>
              <template v-for="item in secondList" :key="item.id">
                <li
                  v-if="!item.subImportField"
                  style="cursor: pointer"
                  @click="changeItem(item, true)"
                  :class="{
                    'is-choose': isChooseIds.includes(item.id),
                    disabled: item.disabled && !isChooseIds.includes(item.id)
                  }"
                  >{{ item.name }}</li
                >
                <template v-if="item.subImportField?.length > 0">
                  <span class="links">以下联系方式至少选一种</span>
                  <li
                    v-for="i in item.subImportField"
                    :key="i.id"
                    style="cursor: pointer"
                    @click="changeItem(i, false)"
                    :class="{
                      'is-choose_list': isChooseIds.includes(i.id),
                      disabled: i.disabled && !isChooseIds.includes(i.id)
                    }"
                    >{{ i.name }}</li
                  >
                </template>
              </template>
            </ul>
          </div>
          <div class="choose_footer" @click="openCustomFields"> 新增自定义字段 </div>
        </div>
      </div>
    </div>
  </div>
  <el-icon size="24" color="#52C41A" style="margin-left: 10px" v-if="inputValue != ''"
    ><CircleCheckFilled
  /></el-icon>

  <customFields ref="customFieldsRef" :visible="dialogVisible" @closeDialog="closeDialog" />
</template>
<script setup lang="ts">
import { ArrowDown, ArrowRight, CircleClose, CircleCheckFilled } from "@element-plus/icons-vue"
import { ClickOutside as vClickOutside } from "element-plus"
import customFields from "./customFields.vue"

const props = defineProps({
  importType: {
    type: String,
    default: ""
  },
  code: {
    type: String,
    default: ""
  },
  customerSystemFields: {
    type: Array,
    default: () => []
  },
  contactSystemFields: {
    type: Array,
    default: () => []
  },
  customerNoMustFields: {
    type: Array,
    default: () => []
  },
  contactNoMustFields: {
    type: Array,
    default: () => []
  },
  followSystemFields: {
    type: Array,
    default: () => []
  },
  followNoMustFields: {
    type: Array,
    default: () => []
  },
  userSelectList: {
    type: Array,
    default: () => []
  }
})
const cascadeType = ref<any>([])
watch(
  () => props.importType,
  (nV: any, oV: any) => {
    console.log("props.importType1", nV)
    if (props.importType == "1") {
      cascadeType.value = [
        {
          field: "1",
          name: "客户"
        },
        {
          field: "2",
          name: "联系人"
        }
      ]
    } else if (props.importType == "2") {
      cascadeType.value = [
        {
          field: "1",
          name: "客户"
        },
        {
          field: "2",
          name: "联系人"
        },
        {
          field: "3",
          name: "跟进记录"
        }
      ]
    }
  },
  {
    immediate: true
  }
)

/**
 * 切换
 */
const isDropdownVisible = ref(false)

//切换下拉框
const toggleDropdown = () => {
  isDropdownVisible.value = !isDropdownVisible.value
  if (!isDropdownVisible.value && inputValue.value === "") {
    isShowTwoMenu.value = false
    chooseTypeIndex.value = null
  }
  //回显选中数据,并跳转到指定位置
  if (isDropdownVisible.value) {
    if (inputValue.value) {
      chooseTypeIndex.value = selectImportType.value
      isMust.value = tabType.value
      getSecondListByTab(tabType.value, selectImportType.value)
      elScrollIntoView()
    }
  }
}

// 滚动到选中位置
const elScrollIntoView = async () => {
  await nextTick()
  const element =
    document.querySelectorAll(".is-choose_list")[0] || document.querySelectorAll(".is-choose")[0]
  if (element) {
    element.scrollIntoView({ block: "end" })
  }
}

const closeOrder = () => {
  emit("updateSelectFields", props.code, isChooseItem.value)
  isDropdownVisible.value = false
}

//切换类型
const chooseTypeIndex = ref<any>(null)
//展示二级菜单
const isShowTwoMenu = ref(false)

const secondList = ref<any>([])
const chooseType = (field: any) => {
  chooseTypeIndex.value = field
  isShowTwoMenu.value = true
  isMust.value = "1"
  // 获取二级菜单数据 必须数据
  getSecondList(field)
}

// 获取二级菜单数据 必须数据
const getSecondList = (type: any) => {
  switch (type) {
    case "1":
      secondList.value = props.customerSystemFields || []
      break
    case "2":
      secondList.value = props.contactSystemFields || []
      break
    case "3":
      secondList.value = props.followSystemFields || []
      break
  }
}

// 是不是展示删除按钮
const isShowDelete = ref(false)
const mouseShowDelete = () => {
  isShowDelete.value = true
}
const noShowDelete = () => {
  isShowDelete.value = false
}

//删除input数据
const deleteInput = () => {
  inputValue.value = ""
  isChooseIds.value = []
  isChooseItem.value = []
  emit("updateSelectFields", props.code, isChooseItem.value)
  isDropdownVisible.value = false
}

//展示input结果
const inputValue = ref("")
const emit = defineEmits(["updateSelectFields"])

/**
 * 单选
 */

const isChooseItem = ref<any>([])
const isChooseIds = ref<any>([])
//选中的一级菜单
const selectImportType = ref("")
const tabType = ref("")
const changeItem = (item: any, flag: boolean) => {
  if (flag) {
    //单选
    //禁用不允许点击
    if (item.disabled) {
      return
    }
    isChooseItem.value = [{ id: item.id, name: item.name, flag: flag }]
  } else {
    //多选
    //禁用不允许点击
    if (item.disabled && !isChooseIds.value.includes(item.id)) {
      return
    }
    //排除单选
    isChooseItem.value = isChooseItem.value.filter((e: any) => !e.flag)
    let ids = isChooseItem.value.map((j: any) => j.id)
    //点击可以多选
    if (ids.includes(item.id)) {
      isChooseItem.value = isChooseItem.value.filter((j: any) => j.id !== item.id)
    } else {
      isChooseItem.value.push({ id: item.id, name: item.name, flag: flag })
    }
  }
  emit("updateSelectFields", props.code, isChooseItem.value)

  //回显数据位置
  selectImportType.value = item.importType
  tabType.value = isMust.value
  //回显input数据
  inputValue.value = ""
  if (isChooseItem.value.length > 0) {
    let list: any = []
    isChooseItem.value.forEach((e: any) => list.push(e.name))
    isChooseIds.value = isChooseItem.value.map((i: any) => i.id)
    inputValue.value = list.join(",")
  } else {
    inputValue.value = ""
    isChooseIds.value = []
  }
  //单选选择后直接关闭弹框
  if (flag) {
    isDropdownVisible.value = false
  }
}

/**
 * 多选
 */
// const isChooseItemList = ref<any>([])
// const changeItemList = (item: any) => {
//   //禁用
//   if (item.disabled) {
//     return
//   }
//   //多选清除单选选项
//   if (isChooseItem.value != "") {
//     isChooseItem.value = ""
//   }
//   emit("updateSelectFields", item.id, false)
//   //多选
//   if (isChooseItemList.value.includes(item.id)) {
//     isChooseItemList.value = isChooseItemList.value.filter((j: any) => j !== item.id)
//   } else {
//     isChooseItemList.value.push(item.id)
//   }
//   //回显input数据
//   if (isChooseItemList.value.length > 0) {
//     let list: any = []
//     secondList.value.forEach((item: any) => {
//       if (item.subImportField?.length > 0) {
//         item.subImportField.forEach((i: any) => {
//           if (isChooseItemList.value.includes(i.id)) {
//             list.push(i.name)
//           }
//         })
//       }
//     })
//     inputValue.value = list.join(",")
//   } else {
//     inputValue.value = ""
//   }
// }

// 切换必须非必选
const isMust = ref<any>("1")
const MustList = ref([
  {
    name: "必须",
    id: "1"
  },
  {
    name: "非必须",
    id: "2"
  }
])
const changeMustTab = (id) => {
  isMust.value = id
  getSecondListByTab(id, chooseTypeIndex.value)
}

const getSecondListByTab = (id, chooseTypeIndex) => {
  if (chooseTypeIndex == "1") {
    switch (id) {
      case "1":
        secondList.value = props.customerSystemFields
        break
      case "2":
        secondList.value = props.customerNoMustFields
        break
    }
  } else if (chooseTypeIndex == "2") {
    switch (id) {
      case "1":
        secondList.value = props.contactSystemFields
        break
      case "2":
        secondList.value = props.contactNoMustFields
        break
    }
  } else if (chooseTypeIndex == "3") {
    switch (id) {
      case "1":
        secondList.value = props.followSystemFields
        break
      case "2":
        secondList.value = props.followNoMustFields
        break
    }
  }
}

// 打开自定义弹框
const dialogVisible = ref(false)
const openCustomFields = () => {
  dialogVisible.value = true
}
const closeDialog = () => {
  dialogVisible.value = false
}
</script>
<style scoped lang="scss">
.drop_down {
  position: relative;
  width: 80%;
  .cascade-input {
    height: 40px;
    padding: 0px 12px;
    border: 1px solid #ccc;
    border-radius: 4px;
    cursor: pointer;
    user-select: none;
    display: flex;
    justify-content: space-between;
    align-items: center;
    position: relative;
    .cascade_choose {
      color: #333333;
      font-size: 16px;
      font-weight: 400;
    }
  }
  .cascade-input.is-active {
    border-color: #409eff;
  }
  .down {
    transition: all 0.4s;
  }
  .is-rotate {
    transform: rotate(180deg);
  }
  .cascade-dropdown {
    position: absolute;
    top: 40px;
    left: 0px;
    z-index: 99;
    display: inline-block;
    box-shadow: 0 2px 9px 0 #c8c9cc80;
    height: 291px;
    background: #fff;
    border-radius: 2px;
    border: 1px solid #ebedf0;
    border-radius: 4px;
    margin-top: 4px;
    .active {
      background: #f7f8fa;
    }
    .cascade_item {
      width: 240px;
      height: 36px;
      display: flex;
      justify-content: space-between;
      align-items: center;
      padding: 0px 12px;
      cursor: pointer;
      .cascade_item_name {
        color: #323233;
        font-size: 14px;
        font-weight: 400;
      }
    }
    .choose {
      width: 240px;
      height: 291px;
      border-left: 1px solid #dcdfe6;
      position: relative;
      display: flex;
      flex-direction: column;
      .choose_header {
        width: 100%;
        ul {
          display: flex;
          height: 36px;
          line-height: 36px;
          border-bottom: 1px solid #dedede;
          box-sizing: border-box;
          padding-left: 10px;
          li {
            margin-right: 20px;
            color: #323233;
            font-size: 14px;
            font-weight: 500;
            &:hover {
              cursor: pointer;
            }
          }
        }
        .is-must {
          color: #477fff;
          border-bottom: 1px solid #477fff;
        }
      }
      .choose_content {
        // padding-left: 12px;
        flex: 1;
        overflow: auto;
        margin-bottom: 40px;
        ul {
          li {
            height: 32px;
            line-height: 32px;
            padding-left: 12px;
          }
          .is-choose {
            background: #f7f8fa;
            color: black !important;
          }
          .is-choose_list {
            background: #f7f8fa;
            color: black !important;
          }
          .disabled {
            color: #bebebe;
            cursor: not-allowed !important;
          }
        }
        .links {
          color: #477fff;
          font-size: 12px;
          font-family: "苹方";
          padding: 5px 0px 5px 12px;
        }
      }
      .choose_footer {
        position: absolute;
        left: 0;
        bottom: 0;
        width: 92%;
        height: 40px;
        line-height: 40px;
        text-align: center;
        border-top: 1px solid #dcdfe6;
        color: #477fff;
        font-size: 14px;
        font-weight: 400;
        margin: 0px 10px;
      }
    }
  }
}
</style>

二 使用

  <table border width="100%" class="match_table">
      <thead>
        <tr class="match_tr match_thead">
          <td class="march_th_left">Excel 字段</td>
          <td class="march_th_right">系统字段</td>
        </tr>
      </thead>
      <tbody>
        <tr v-for="item in excelFields" :key="item.code" class="match_tr">
          <td class="march_th_left"> {{ item.name }}</td>
          <td class="march_th_right">
            <fieldSelect
              :importType="props.importType"
              :customerSystemFields="customerSystemFields"
              :customerNoMustFields="customerNoMustFields"
              :contactSystemFields="contactSystemFields"
              :contactNoMustFields="contactNoMustFields"
              :followSystemFields="followSystemFields"
              :followNoMustFields="followNoMustFields"
              @update-select-fields="updateSelectFields"
              :code="item.code"
            />
          </td>
        </tr>
      </tbody>
    </table>



const updateSelectFields = (code, value) => {
  userSelectList.value = []
  everySelectList.value[code] = value

  for (let key in everySelectList.value) {
    let list = everySelectList.value[key]
    userSelectList.value = Array.from(new Set([...userSelectList.value, ...list]))
  }
  let data = userSelectList.value.map((e: any) => {
    return e.id
  })
  updateMatchs(everySelectList.value)
  // 更新系统字段数据禁用
  let allFields
  if (props.importType == "1") {
    allFields = [
      ...customerSystemFields.value,
      ...customerNoMustFields.value,
      ...contactSystemFields.value,
      ...contactNoMustFields.value
    ]
  } else if (props.importType == "2") {
    allFields = [
      ...customerSystemFields.value,
      ...customerNoMustFields.value,
      ...contactSystemFields.value,
      ...contactNoMustFields.value,
      ...followSystemFields.value,
      ...followNoMustFields.value
    ]
  }

  // if (ismust == 1) {
  allFields.forEach((item) => {
    if (item.subImportField) {
      item.subImportField.forEach((item1) => {
        if (data.includes(item1.id)) {
          item1.disabled = true
        } else {
          item1.disabled = false
        }
      })
    } else {
      if (data.includes(item.id)) {
        item.disabled = true
      } else {
        item.disabled = false
      }
    }
  })
  // } else {
  contactSystemFields.value.forEach((item) => {
    if (data.includes(item.id)) {
      item.disabled = true
    } else {
      item.disabled = false
    }
  })
  // }
}



const getSystemFields = () => {
  //客户
  customerSystemFields.value = [
    {
      name: "称谓",
      id: 21,
      importType: "1"
    },
    {
      name: "生日",
      id: 22,
      importType: "1"
    },
    {
      name: "生日",
      id: 221,
      importType: "1"
    },
    {
      name: "生日",
      id: 222,
      importType: "1"
    },
    {
      name: "生日",
      id: 223,
      importType: "1"
    },
    {
      name: "生日",
      id: 224,
      importType: "1"
    },
    {
      name: "生日",
      id: 225,
      importType: "1"
    },
    {
      name: "联系方式",
      id: 23,
      subImportField: [
        {
          name: "邮箱",
          id: 24,
          importType: "1"
        },
        {
          name: "手机",
          id: 25,
          importType: "1"
        },
        {
          name: "电话",
          id: 26,
          importType: "1"
        },
        {
          name: "电话",
          id: 27,
          importType: "1"
        },
        {
          name: "电话",
          id: 28,
          importType: "1"
        },
        {
          name: "电话",
          id: 29,
          importType: "1"
        },
        {
          name: "电话",
          id: 30,
          importType: "1"
        }
      ]
    }
  ]
  customerNoMustFields.value = [
    {
      name: "域名",
      id: 31,
      importType: "1"
    },
    {
      name: "意向客户",
      id: 32,
      importType: "1"
    }
  ]
  contactSystemFields.value = [
    {
      name: "传真",
      id: 11,
      importType: "2"
    },
    {
      name: "职务",
      id: 12,
      importType: "2"
    }
  ]
  contactNoMustFields.value = [
    {
      name: "部门",
      id: 41,
      importType: "2"
    },
    {
      name: "职务",
      id: 42,
      importType: "2"
    }
  ]
  followSystemFields.value = [
    {
      name: "跟进时间",
      id: 51,
      importType: "3"
    },
    {
      name: "跟进方式",
      id: 52,
      importType: "3"
    }
  ]
  followNoMustFields.value = [
    {
      name: "跟进状态",
      id: 61,
      importType: "3"
    }
  ]
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值