Browse Source

Merge branch 'dev' of https://git.citupro.com/zhengnaiwen_citu/menduner into dev

Xiao_123 7 tháng trước cách đây
mục cha
commit
773b59d713

+ 5 - 0
components.d.ts

@@ -7,6 +7,9 @@ export {}
 
 declare module 'vue' {
   export interface GlobalComponents {
+    2: typeof import('./src/components/jobTypeCard/index copy 2.vue')['default']
+    ALevel2: typeof import('./src/components/jobTypeCard/a-level2.vue')['default']
+    ALevel3: typeof import('./src/components/jobTypeCard/a-level3.vue')['default']
     AreaSelect: typeof import('./src/components/AreaSelect/index.vue')['default']
     Autocomplete: typeof import('./src/components/FormUI/autocomplete/index.vue')['default']
     Cascade: typeof import('./src/components/FormUI/cascade/index.vue')['default']
@@ -41,6 +44,8 @@ declare module 'vue' {
     Introduction: typeof import('./src/components/Enterprise/components/introduction.vue')['default']
     Item: typeof import('./src/components/Position/item.vue')['default']
     JobTypeCard: typeof import('./src/components/jobTypeCard/index.vue')['default']
+    Level2: typeof import('./src/components/jobTypeCard/level2.vue')['default']
+    Level3: typeof import('./src/components/jobTypeCard/level3.vue')['default']
     ListGroup: typeof import('./src/components/FormUI/nestedListGroup/components/listGroup.vue')['default']
     Loading: typeof import('./src/components/Loading/index.vue')['default']
     LongCompany: typeof import('./src/components/Position/longCompany.vue')['default']

+ 227 - 0
src/components/jobTypeCard/a-level2.vue

@@ -0,0 +1,227 @@
+<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="rightContent">
+          <div
+            v-if="!rightObj.data.children?.length"
+            style="width: 100%; text-align: center; color: gray; margin-top: 100px;"
+          >暂无数据</div>
+          <div 
+            v-else 
+            :class="['jobItem', {'active': selectItems.includes(val.id)}]"
+            v-for="val in rightObj.data.children" 
+            :key="val.id" 
+            @click="handleClick(val)"
+          >
+          {{ val.nameCn }}</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>

+ 232 - 0
src/components/jobTypeCard/a-level3.vue

@@ -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>

+ 1 - 0
src/components/jobTypeCard/index.vue

@@ -1,3 +1,4 @@
+<!-- 三级结构 -->
 <template>
   <div class="floatCard d-flex" style="z-index: 999" @mouseleave="handleMouseLeave">
     <v-card class="card">

+ 1 - 1
src/views/recruit/personal/company/components/areaType.vue

@@ -51,7 +51,7 @@ const getAreaList = async () => {
   })
   items.value = [{ id: -1, name: '全国', active: true }, ...list]
   // 刷新回显
-  if (routeQuery.areaIds) {
+  if (routeQuery?.areaIds) {
     const obj = items.value.find(e => e.id === routeQuery.areaIds)
     items.value.map(e => e.active = false)
     if (obj) obj.active = true

+ 4 - 4
src/views/recruit/personal/position/components/areaCascader/index.vue

@@ -49,7 +49,8 @@
 import CtDialog from '@/components/CtDialog'
 import textUI from '@/components/FormUI/TextInput'
 import { getDict } from '@/hooks/web/useDictionaries'
-import { inject, reactive, ref } from 'vue'
+import { reactive, ref } from 'vue'
+import { useRoute } from 'vue-router'; const route = useRoute()
 defineOptions({ name:'common-components-areaTree'})
 const emits = defineEmits(['checkedInput'])
 
@@ -72,13 +73,12 @@ const num = 10
 
 let treeList = ref() 
 const show = ref(false) 
-const query = inject('routeQuery')
 // 获取区域数据
 getDict('areaTreeData', {}, 'areaTreeData').then(({ data }) => {
   const arr = data?.length && data || []
   treeList.value = [arr]
-  if (query && query.city) { // 刷新回显
-    const city = query.city
+  if (route.query && route.query.city) { // 刷新回显
+    const city = route.query.city
     const levelCountArr = city.split('__')
     levelCountArr?.forEach((areaIds, levelIndex) => {
       const idArr = areaIds.split('_')

+ 0 - 1
src/views/recruit/personal/position/components/conditionFilter/commonPath.vue

@@ -28,7 +28,6 @@ import { ref, computed, watch } from 'vue';
 
 defineOptions({name: 'conditionFilter-JobType'})
 const emits = defineEmits(['inputChange'])
-// const query = inject('routeQuery')
 const props = defineProps({
   isRefreshEmit: { // 刷新是否触发inputChange
     type: Boolean,