支持单选 和多选同时存在
一封装下拉框组件
<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"
}
]
}