|
@@ -0,0 +1,342 @@
|
|
|
|
+<script setup lang="ts">
|
|
|
|
+import avatar from '@/assets/images/avatar.png'
|
|
|
|
+
|
|
|
|
+interface SelectProps {
|
|
|
|
+ //选完后数据
|
|
|
|
+ modelValue?: any
|
|
|
|
+ //标题数据
|
|
|
|
+ tabsList: any
|
|
|
|
+ //树详情获取接口
|
|
|
|
+ request: (...args: any) => Promise<any>
|
|
|
|
+ // label 的属性名
|
|
|
|
+ labelKey?: string
|
|
|
|
+ // value 的属性名
|
|
|
|
+ valueKey?: string
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// const props = defineProps<Props>()
|
|
|
|
+const props = withDefaults(defineProps<SelectProps>(), {
|
|
|
|
+ tabsList: [
|
|
|
|
+ {
|
|
|
|
+ label: '部门',
|
|
|
|
+ children: []
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ label: '角色',
|
|
|
|
+ children: []
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ label: '岗位',
|
|
|
|
+ children: []
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ label: '分组',
|
|
|
|
+ children: []
|
|
|
|
+ }
|
|
|
|
+ ],
|
|
|
|
+ labelKey: 'label',
|
|
|
|
+ valueKey: 'id'
|
|
|
|
+})
|
|
|
|
+
|
|
|
|
+const titleList = ref<any>([])
|
|
|
|
+titleList.value = JSON.parse(JSON.stringify(props.tabsList))
|
|
|
|
+
|
|
|
|
+const emits = defineEmits(['update:modelValue', 'clickChild'])
|
|
|
|
+
|
|
|
|
+const visibleDialog = ref<boolean>(false)
|
|
|
|
+
|
|
|
|
+const selectValue = computed({
|
|
|
|
+ get: () => props.modelValue,
|
|
|
|
+ set: value => {
|
|
|
|
+ emits('update:modelValue', value)
|
|
|
|
+ }
|
|
|
|
+})
|
|
|
|
+
|
|
|
|
+const customerOption = ref<any>(null)
|
|
|
|
+const handleSelectCustomer = (res: any) => {
|
|
|
|
+ customerOption.value.blur()
|
|
|
|
+ visibleDialog.value = true
|
|
|
|
+ console.log('selectValue', selectValue.value)
|
|
|
|
+ titleList.value[0].children = selectValue.value
|
|
|
|
+
|
|
|
|
+ setTimeout(() => {
|
|
|
|
+ const deflist = ref<any>([])
|
|
|
|
+ titleList.value[0].children.forEach((item: any) => {
|
|
|
|
+ deflist.value.push(item.id)
|
|
|
|
+ })
|
|
|
|
+ treeRef.value.setCheckedKeys(deflist.value)
|
|
|
|
+ }, 1000)
|
|
|
|
+
|
|
|
|
+ //处理数据回显逻辑
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const saveDialog = () => {
|
|
|
|
+ selectValue.value = []
|
|
|
|
+ setTimeout(() => {
|
|
|
|
+ titleList.value.forEach((element: any) => {
|
|
|
|
+ if (element.children.length > 0) {
|
|
|
|
+ element.children.forEach((elem: any) => {
|
|
|
|
+ selectValue.value.push(elem)
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+ })
|
|
|
|
+ emits('clickChild', titleList.value)
|
|
|
|
+ visibleDialog.value = false
|
|
|
|
+ }, 100)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const search = ref<any>('')
|
|
|
|
+
|
|
|
|
+const handleClick = (e: any) => {}
|
|
|
|
+
|
|
|
|
+watch(search, val => {
|
|
|
|
+ treeRef.value!.filter(val)
|
|
|
|
+})
|
|
|
|
+
|
|
|
|
+const defaultProps = {
|
|
|
|
+ children: 'children',
|
|
|
|
+ label: props.labelKey
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const activeName = ref<any>(0)
|
|
|
|
+
|
|
|
|
+const treeRef = ref<any>(null)
|
|
|
|
+
|
|
|
|
+const data = ref<any>([])
|
|
|
|
+
|
|
|
|
+const filterNode = (value: string, data: any) => {
|
|
|
|
+ if (!value) return true
|
|
|
|
+ return data.label.includes(value)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const handleCheckChange = (data: any, checked: boolean, indeterminate: boolean) => {
|
|
|
|
+ initList()
|
|
|
|
+ let checkValue = treeRef.value.getCheckedNodes()
|
|
|
|
+ checkValue.forEach((element: any) => {
|
|
|
|
+ if (!element.children) {
|
|
|
|
+ titleList.value[activeName.value].children.push(element)
|
|
|
|
+ }
|
|
|
|
+ })
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const initList = () => {
|
|
|
|
+ titleList.value = JSON.parse(JSON.stringify(props.tabsList))
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const request = (where?: any) => {
|
|
|
|
+ if (typeof props?.request === 'function') {
|
|
|
|
+ props
|
|
|
|
+ .request({
|
|
|
|
+ ...where
|
|
|
|
+ })
|
|
|
|
+ .then((res: any) => {
|
|
|
|
+ data.value = res.infos || res.list || res.records || res.data || res.info
|
|
|
|
+ data.value.forEach((item: any) => {
|
|
|
|
+ const temp =
|
|
|
|
+ Array.isArray(selectValue.value) && selectValue.value.find(x => x[props.valueKey] === item[props.valueKey])
|
|
|
|
+
|
|
|
|
+ console.log('temp', temp)
|
|
|
|
+ temp && treeRef.value.setCheckedKeys(item.id)
|
|
|
|
+ })
|
|
|
|
+ })
|
|
|
|
+ .catch((e: any) => {
|
|
|
|
+ console.warn(e)
|
|
|
|
+ })
|
|
|
|
+ .finally(() => {})
|
|
|
|
+ } else if (Array.isArray(props?.request)) {
|
|
|
|
+ data.value = where
|
|
|
|
+ console.log('获取树接口', data.value)
|
|
|
|
+ } else {
|
|
|
|
+ console.warn('tree组件数据格式错误')
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+request()
|
|
|
|
+
|
|
|
|
+const clearAll = () => {
|
|
|
|
+ initList()
|
|
|
|
+ treeRef.value.setCheckedKeys([], false)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const delItem = (e: any, i: any) => {
|
|
|
|
+ e.splice(i, 1)
|
|
|
|
+ const deflist = ref<any>([])
|
|
|
|
+ e.forEach((item: any) => {
|
|
|
|
+ deflist.value.push(item.id)
|
|
|
|
+ })
|
|
|
|
+ treeRef.value.setCheckedKeys(deflist.value)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const currentValues = computed(() => {
|
|
|
|
+ if (selectValue.value == null) {
|
|
|
|
+ return selectValue.value ?? []
|
|
|
|
+ }
|
|
|
|
+ return selectValue.value.slice(0, 5)
|
|
|
|
+})
|
|
|
|
+
|
|
|
|
+const onItemClear = (item: any) => {
|
|
|
|
+ const list = [...(currentValues.value as Array<any>)]
|
|
|
|
+ const index = list.findIndex(x => x[props.labelKey] === item[props.labelKey])
|
|
|
|
+ list.splice(index, 1)
|
|
|
|
+ selectValue.value = list
|
|
|
|
+}
|
|
|
|
+</script>
|
|
|
|
+
|
|
|
|
+<template>
|
|
|
|
+ <div class="table-select-container is-multiple" @click="handleSelectCustomer">
|
|
|
|
+ <el-input ref="customerOption" :modelValue="''" @focus="handleSelectCustomer">
|
|
|
|
+ <!-- <template v-if="$slots.prefix" #prefix>
|
|
|
|
+ <slot name="prefix"></slot>
|
|
|
|
+ </template>
|
|
|
|
+ <template #suffix>
|
|
|
|
+ <div class="select-suffix">
|
|
|
|
+ <ElIcon class="select-clear" @click.stop="onClear">
|
|
|
|
+ <slot name="clearIcon">
|
|
|
|
+ <CircleClose />
|
|
|
|
+ </slot>
|
|
|
|
+ </ElIcon>
|
|
|
|
+ </div>
|
|
|
|
+ </template> -->
|
|
|
|
+ </el-input>
|
|
|
|
+ <div class="table-multiple-tag">
|
|
|
|
+ <el-tag
|
|
|
|
+ type="success"
|
|
|
|
+ size="small"
|
|
|
|
+ disable-transitions
|
|
|
|
+ :closable="true"
|
|
|
|
+ style="margin-right: 5px"
|
|
|
|
+ v-for="item in currentValues"
|
|
|
|
+ :key="item[labelKey]"
|
|
|
|
+ @close="onItemClear(item)"
|
|
|
|
+ >
|
|
|
|
+ {{ item[labelKey] }}
|
|
|
|
+ </el-tag>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <el-dialog title="选择客户" v-model="visibleDialog" height="800px" append-to-body destroy-on-close>
|
|
|
|
+ <div class="flex pt-5 pb-5">
|
|
|
|
+ <div class="w-1/2 mr-5">
|
|
|
|
+ <el-input v-model="search" placeholder="请输入关键词">
|
|
|
|
+ <template #append>
|
|
|
|
+ <el-button>
|
|
|
|
+ <el-icon size="18" slot="append"><component is="search"></component></el-icon>
|
|
|
|
+ </el-button>
|
|
|
|
+ </template>
|
|
|
|
+ </el-input>
|
|
|
|
+ <div class="left_border mt-5 h-400px">
|
|
|
|
+ <el-tabs v-model="activeName" @tab-click="handleClick" stretch>
|
|
|
|
+ <el-tab-pane v-for="(item, index) in titleList" :label="item.label" :name="index"> </el-tab-pane>
|
|
|
|
+ </el-tabs>
|
|
|
|
+ <el-tree
|
|
|
|
+ ref="treeRef"
|
|
|
|
+ style="max-width: 600px"
|
|
|
|
+ :data="data"
|
|
|
|
+ :props="defaultProps"
|
|
|
|
+ show-checkbox
|
|
|
|
+ :check-strictly="false"
|
|
|
|
+ node-key="id"
|
|
|
|
+ :filter-node-method="filterNode"
|
|
|
|
+ @check-change="handleCheckChange"
|
|
|
|
+ />
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ <div class="w-1/2 ml-5">
|
|
|
|
+ <div class="flex justify-between items-center h-30px mb-5">
|
|
|
|
+ <div>已选</div>
|
|
|
|
+ <el-button text type="danger" @click="clearAll">清空列表</el-button>
|
|
|
|
+ </div>
|
|
|
|
+ <div class="left_border h-400px p-3">
|
|
|
|
+ <div v-for="(itm, idx) in titleList" :key="idx">
|
|
|
|
+ <div class="text-lg flex items-center" v-if="itm.children.length > 0">
|
|
|
|
+ <el-icon><Service /></el-icon>
|
|
|
|
+ {{ itm.label }}
|
|
|
|
+ </div>
|
|
|
|
+ <div
|
|
|
|
+ v-for="(it, id) in itm.children"
|
|
|
|
+ class="flex justify-between mt-2 items-center right-border pb-2 ml-5"
|
|
|
|
+ v
|
|
|
|
+ >
|
|
|
|
+ <div class="flex items-center">
|
|
|
|
+ <el-image class="w-40px h-40px mr-2" :src="it.picture ? it.picture : avatar"></el-image>
|
|
|
|
+ <div>
|
|
|
|
+ <div class="text-base">{{ it.label }}</div>
|
|
|
|
+ <div>{{ it.childrenName }}</div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ <div>
|
|
|
|
+ <Delete style="width: 1em; height: 1em; margin-right: 8px" @click="delItem(itm.children, id)" />
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <template #footer>
|
|
|
|
+ <el-button @click="visibleDialog = false">取 消</el-button>
|
|
|
|
+ <el-button type="primary" @click="saveDialog">确 定</el-button>
|
|
|
|
+ </template>
|
|
|
|
+ </el-dialog>
|
|
|
|
+</template>
|
|
|
|
+
|
|
|
|
+<style scoped lang="scss">
|
|
|
|
+.left_border {
|
|
|
|
+ border: 1px solid #e5e5e5;
|
|
|
|
+}
|
|
|
|
+.right-border {
|
|
|
|
+ border-bottom: 1px solid #e5e5e5;
|
|
|
|
+}
|
|
|
|
+.table-multiple-tag {
|
|
|
|
+ position: relative;
|
|
|
|
+ top: 0px;
|
|
|
|
+ width: calc(100% - 24px);
|
|
|
|
+ height: 100%;
|
|
|
|
+ min-height: 34px;
|
|
|
|
+ z-index: 2;
|
|
|
|
+ padding: 0px 10px 2px 10px;
|
|
|
|
+ box-sizing: border-box;
|
|
|
|
+ &.is-disabled {
|
|
|
|
+ cursor: not-allowed;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+.table-select-container {
|
|
|
|
+ width: 100%;
|
|
|
|
+ position: relative;
|
|
|
|
+
|
|
|
|
+ .select-clear {
|
|
|
|
+ position: absolute;
|
|
|
|
+ right: 10px;
|
|
|
|
+ top: 49%;
|
|
|
|
+ transform: translateY(-50%);
|
|
|
|
+ cursor: pointer;
|
|
|
|
+ border-radius: 50%;
|
|
|
|
+ overflow: hidden;
|
|
|
|
+ z-index: 2;
|
|
|
|
+ opacity: 0;
|
|
|
|
+ &:hover {
|
|
|
|
+ opacity: 100;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ &.is-multiple {
|
|
|
|
+ :deep(.el-input) {
|
|
|
|
+ position: absolute;
|
|
|
|
+ width: 100%;
|
|
|
|
+ height: 100%;
|
|
|
|
+ top: 0;
|
|
|
|
+ left: 0;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ &.is-visible {
|
|
|
|
+ :deep(.el-input:not(.is-disabled) .el-input__wrapper) {
|
|
|
|
+ box-shadow: 0 0 0 1px var(--el-color-primary) inset;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ &:hover {
|
|
|
|
+ .select-clear {
|
|
|
|
+ opacity: 1;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+</style>
|