|
@@ -0,0 +1,232 @@
|
|
|
+<!-- 三级结构 -->
|
|
|
+<template>
|
|
|
+ <div class="floatCard d-flex" style="z-index: 999" @mouseleave="handleMouseLeave">
|
|
|
+ <v-card class="card">
|
|
|
+ <div class="leftCard">
|
|
|
+ <div
|
|
|
+ class="leftCardItem"
|
|
|
+ :class="{'leftIndexAct': index === leftIndex,'leftSelectedAct': leftEchoIdArr?.length ? leftEchoIdArr.includes(item.id) : false}"
|
|
|
+ v-for="(item, index) in items" :key="item.id"
|
|
|
+ @mouseover="handleMouseOver(item, index)"
|
|
|
+ @click="leftHandleClick(item, index)"
|
|
|
+ >
|
|
|
+ <div class="rowItem d-flex">
|
|
|
+ <span class="categoryName">{{ item.nameCn }}</span>
|
|
|
+ <span v-if="item.id !== '-1'" class="mdi mdi-menu-right"></span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </v-card>
|
|
|
+ <v-card v-if="rightObj.show" class="card rightCardBox">
|
|
|
+ <div class="rightCard">
|
|
|
+ <div class="categoryName">{{ rightObj.data.nameCn }}</div>
|
|
|
+ <div v-for="(item, index) in rightObj.data.children" :key="item.id">
|
|
|
+ <v-divider v-if="index" class="divider"></v-divider>
|
|
|
+ <div class="rowItem d-flex">
|
|
|
+ <div class="categoryName2">{{ item.nameCn }}</div>
|
|
|
+ <div class="rightContent">
|
|
|
+ <div v-if="!item.children?.length"></div>
|
|
|
+ <div
|
|
|
+ v-else
|
|
|
+ :class="['jobItem', {'active': selectItems.includes(val.id)}]"
|
|
|
+ v-for="val in item.children"
|
|
|
+ :key="val.id"
|
|
|
+ @click="handleClick(val)"
|
|
|
+ >
|
|
|
+ {{ val.nameCn }}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </v-card>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+import { getDict } from '@/hooks/web/useDictionaries'
|
|
|
+import { buryingPoint } from '@/hooks/web/buryingPoint'
|
|
|
+import { reactive, ref } from 'vue';
|
|
|
+defineOptions({ name:'common-components-jobTypeCard'})
|
|
|
+
|
|
|
+const emits = defineEmits(['handleJobClick', 'jobClick'])// 定义一个或多个自定义事件
|
|
|
+
|
|
|
+const props = defineProps({
|
|
|
+ clearable: { // 不限
|
|
|
+ type: Boolean,
|
|
|
+ default: false
|
|
|
+ },
|
|
|
+ echo: { // 是否回显
|
|
|
+ type: Boolean,
|
|
|
+ default: false
|
|
|
+ },
|
|
|
+ isSingle: { // 是否单选
|
|
|
+ type: Boolean,
|
|
|
+ default: false
|
|
|
+ },
|
|
|
+ isBuryingPoint: { // 是否埋点
|
|
|
+ type: Boolean,
|
|
|
+ default: false
|
|
|
+ },
|
|
|
+ select: { // 已选中
|
|
|
+ type: Array,
|
|
|
+ default: () => []
|
|
|
+ }
|
|
|
+})
|
|
|
+const selectItems = ref([])
|
|
|
+let leftEchoIdArr = ref([])
|
|
|
+let loading = ref(false)
|
|
|
+
|
|
|
+// 回显
|
|
|
+if (props.select.length) selectItems.value = props.select.map(e => e)
|
|
|
+// 回显左侧level:1选中
|
|
|
+const echoLeft = async () => {
|
|
|
+ if (!props.echo || !items.value?.length) return loading.value = false
|
|
|
+ if (!selectItems.value.length) {
|
|
|
+ leftEchoIdArr.value = ref([])
|
|
|
+ loading.value = false
|
|
|
+ return
|
|
|
+ }
|
|
|
+ traverse(items.value)
|
|
|
+ try {
|
|
|
+ loading.value = true
|
|
|
+ await traverse(items.value)
|
|
|
+ } catch (error) {
|
|
|
+ loading.value = false
|
|
|
+ }
|
|
|
+ function traverse(nodes) {
|
|
|
+ for (const node of nodes) {
|
|
|
+ if (selectItems.value.includes(node.id)) {
|
|
|
+ const fId = node.path ? node.path.split(',')[0] : null
|
|
|
+ if (fId) leftEchoIdArr.value = leftEchoIdArr.value.length ? [...leftEchoIdArr.value, fId] : [fId]
|
|
|
+ }
|
|
|
+ if (node.children) {
|
|
|
+ traverse(node.children)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+let items = ref([])
|
|
|
+getDict('positionTreeData', null, 'positionTreeData').then(({ data }) => {
|
|
|
+ data = data?.length && data || []
|
|
|
+ items.value = data
|
|
|
+ if (props.clearable) items.value = [{ id: '-1', nameCn: '不限', nameEn: 'Unrestricted' }, ...data]
|
|
|
+ else items.value = data
|
|
|
+ echoLeft()
|
|
|
+})
|
|
|
+
|
|
|
+// 职位点击
|
|
|
+const handleClick = async (val) => {
|
|
|
+ if (props.isBuryingPoint) buryingPoint({ id: val.id }) // 埋点
|
|
|
+ const obj = selectItems.value.includes(val.id)
|
|
|
+ if (props.isSingle) {
|
|
|
+ selectItems.value = obj ? [] : [val.id]
|
|
|
+ } else {
|
|
|
+ if (obj) {
|
|
|
+ selectItems.value = selectItems.value.filter(e => e.id !== val.id)
|
|
|
+ } else selectItems.value.push(val.id)
|
|
|
+ }
|
|
|
+ emits('handleJobClick', selectItems.value, selectItems.value.length ? val.nameCn : '')
|
|
|
+ echoLeft()
|
|
|
+}
|
|
|
+// 不限
|
|
|
+const leftHandleClick = async (val) => {
|
|
|
+ if ((val.id + '') === '-1') {
|
|
|
+ leftEchoIdArr.value = []; selectItems.value = []
|
|
|
+ emits('handleJobClick', selectItems.value, selectItems.value.length ? val.nameCn : '')
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 右侧职位信息
|
|
|
+const leftIndex = ref(null)
|
|
|
+const rightObj = reactive({ show: false, data: {} })
|
|
|
+
|
|
|
+const handleMouseOver = (val, index) => { // 鼠标移入
|
|
|
+ if ((val.id + '') === '-1') return
|
|
|
+ leftIndex.value = index
|
|
|
+ rightObj.data = val
|
|
|
+ rightObj.show = true
|
|
|
+}
|
|
|
+const handleMouseLeave = () => { // 鼠标移出
|
|
|
+ rightObj.show = false // true false
|
|
|
+ leftIndex.value = null
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+.active {
|
|
|
+ color: var(--v-primary-base) !important;
|
|
|
+ font-weight: 700;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.v-window) {
|
|
|
+ height: 392px !important;
|
|
|
+}
|
|
|
+.floatCard {
|
|
|
+ .leftIndexAct { color: var(--v-primary-base); }
|
|
|
+ .leftSelectedAct { color: var(--v-primary-base) !important; }
|
|
|
+ .leftCard {
|
|
|
+ height: 242px;
|
|
|
+ width: 172px;
|
|
|
+ margin: 4px 0;
|
|
|
+ overflow-y: auto;
|
|
|
+ .leftCardItem {
|
|
|
+ height: 36px;
|
|
|
+ line-height: 36px;
|
|
|
+ padding: 0 16px;
|
|
|
+ overflow: hidden;
|
|
|
+ cursor: pointer;
|
|
|
+ &:hover {
|
|
|
+ color: var(--v-primary-base);
|
|
|
+ background-color: var(--color-f8);
|
|
|
+ }
|
|
|
+ .categoryName { font-size: 14px; font-family: 微软雅黑; }
|
|
|
+ }
|
|
|
+ .rowItem { justify-content: space-between; }
|
|
|
+ }
|
|
|
+ .rightCardBox { margin-left: 4px; }
|
|
|
+ .rightCard {
|
|
|
+ height: 242px;
|
|
|
+ width: 525px;
|
|
|
+ margin: 4px 0;
|
|
|
+ padding: 0 16px;
|
|
|
+ overflow-y: auto;
|
|
|
+ .categoryName { font-size: 16px; line-height: 28px; margin-top: 6px;}
|
|
|
+ .categoryName2 { font-size: 14px; color: var(--color-666); width: 110px; margin-right: 4px;}
|
|
|
+ .jobItem { font-size: 14px; color: var(--color-333); }
|
|
|
+ .rowItem {
|
|
|
+ padding: 8px 0;
|
|
|
+ }
|
|
|
+ .divider {
|
|
|
+ margin-left: 110px;
|
|
|
+ }
|
|
|
+ .rightContent {
|
|
|
+ flex: 1;
|
|
|
+ div {
|
|
|
+ margin: 4px 28px 2px 0;
|
|
|
+ float: left;
|
|
|
+ cursor: pointer;
|
|
|
+ color: var(--color-333);
|
|
|
+ &:hover {
|
|
|
+ color: var(--v-primary-base);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+// ::v-deep {
|
|
|
+ ::-webkit-scrollbar {
|
|
|
+ width: 4px;
|
|
|
+ height: 10px;
|
|
|
+ // display: none;
|
|
|
+ }
|
|
|
+ ::-webkit-scrollbar-thumb, .temporaryAdd ::-webkit-scrollbar-thumb, .details_edit ::-webkit-scrollbar-thumb {
|
|
|
+ // 滚动条-颜色
|
|
|
+ background: #c3c3c379;
|
|
|
+ }
|
|
|
+ ::-webkit-scrollbar-track, .temporaryAdd ::-webkit-scrollbar-track, .details_edit ::-webkit-scrollbar-track {
|
|
|
+ // 滚动条-底色
|
|
|
+ background: #e5e5e58f;
|
|
|
+ }
|
|
|
+// }
|
|
|
+</style>
|