Browse Source

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

Xiao_123 7 months ago
parent
commit
df8f030b64

+ 3 - 3
src/components/industryTypeCard/index.vue

@@ -58,9 +58,9 @@ const props = defineProps({
   }
   }
 })
 })
 
 
-let items = ref()
-let idChecked = ref([])
-let currentSelect = ref([])
+const items = ref()
+const idChecked = ref([])
+const currentSelect = ref([])
 // 回显
 // 回显
 if (props.currentData.length) currentSelect.value = props.currentData
 if (props.currentData.length) currentSelect.value = props.currentData
 if (props.select.length) idChecked.value = props.select.map(e => e + '') // 数据中的id是字符串
 if (props.select.length) idChecked.value = props.select.map(e => e + '') // 数据中的id是字符串

+ 4 - 1
src/layout/personal/navBar.vue

@@ -107,7 +107,10 @@
       <v-radio-group v-model="radios" :hide-details="true">
       <v-radio-group v-model="radios" :hide-details="true">
         <v-radio v-for="item in enterpriseList" :key="item.enterpriseId" color="primary" :label="item.enterpriseName" :value="item.enterpriseId"></v-radio>
         <v-radio v-for="item in enterpriseList" :key="item.enterpriseId" color="primary" :label="item.enterpriseName" :value="item.enterpriseId"></v-radio>
       </v-radio-group>
       </v-radio-group>
-      <v-btn :loading="loading1" class="ml-6 mb-3" color="primary" variant="text" @click="newRegistering">{{ btnType ? '注册新企业' : '查看申请进度' }}</v-btn>
+      <!-- <v-btn :loading="loading1" color="primary" variant="text" @click="newRegistering">{{ btnType ? '注册新企业' : '查看申请进度' }}</v-btn> -->
+      <div class="text-end pr-5 pt-5">
+        <span class="color-error cursor-pointer text-decoration-underline" @click="newRegistering">{{ btnType ? '都不是我要的?去注册新企业' : '查看申请进度' }}</span>
+      </div>
     </CtDialog>
     </CtDialog>
   </div>
   </div>
 </template>
 </template>

+ 1 - 1
src/locales/zh-CN.js

@@ -41,7 +41,7 @@ export default {
     activatePosition: '激活职位',
     activatePosition: '激活职位',
     suspend: '暂停',
     suspend: '暂停',
     release: '发布',
     release: '发布',
-    clearFilterCriteria: '清空筛选条件',
+    clearFilterCriteria: '重置筛选条件',
     clearSelectedLocation: '清空已选位置',
     clearSelectedLocation: '清空已选位置',
     Unrestricted: '不限',
     Unrestricted: '不限',
     other: '其他',
     other: '其他',

+ 109 - 37
src/views/recruit/personal/position/components/conditionFilter.vue

@@ -1,48 +1,120 @@
 <template>
 <template>
-  <div class="d-flex">
-    <companyIndustry positionIndexPage @inputChange="val => inputChange(ids.industryIds, val)"></companyIndustry>
-    <positionType positionIndexPage @inputChange="val => inputChange(ids.positionId, val)"></positionType>
-    <JobType positionIndexPage @inputChange="val => inputChange(ids.jobType, val)"></JobType>
-    <expType positionIndexPage @inputChange="val => inputChange(ids.expType, val)"></expType>
-    <payScope positionIndexPage @inputChange="val => inputChange(ids.payType, val)"></payScope>
-    <educationType positionIndexPage :isSingle="false" @inputChange="val => inputChange(ids.scale, val)"></educationType>
-    <scale positionIndexPage @inputChange="val => inputChange(ids.eduType, val)"></scale>
-    <financingStatus positionIndexPage @inputChange="val => inputChange(ids.financingStatus, val)"></financingStatus>
+  <div>
+    <div class="d-flex">
+      <template v-for="item in list" :key="item.key">
+        <component
+          :is="item.path"
+          :idName="item.key"
+          :title="item.title"
+          :isSingle="item.isSingle"
+          :dictName="item.dictShow"
+          :info="item"
+          @inputChange="inputChange"
+        ></component>
+      </template>
+    </div>
+    <div v-if="showSelectList?.length">
+      <div style="margin-top: 20px; border-top: 1px solid #eee;">
+        <div v-for="item in showSelectList" :key="item.key" style="margin: 8px 8px 8px 0px;">
+          <template v-if="item.checkedItems?.length">
+            <span style="color: #999;">{{ item.title }}:</span>
+            <v-btn
+              v-for="(val, index) in item.checkedItems" :key="item.key + index"
+              class="mr-3 py-0 px-2"
+              density="comfortable"
+              color="primary" variant="tonal"
+              v-bind="props"
+            >
+              {{ val.text }}
+              <v-icon class="ml-1" style="margin-top: 1px;" @click="deleteChip(item, val)">mdi-close</v-icon>
+            </v-btn>
+          </template>
+        </div>
+      </div>
+      <div class="clearFilterCriteria" @click="emits('reset')">{{ $t('common.clearFilterCriteria') }}</div>
+    </div>
   </div>
   </div>
 </template>
 </template>
 <script setup>
 <script setup>
-import companyIndustry from './conditionFilter/companyIndustry.vue'
-import positionType from './conditionFilter/positionType.vue'
-import JobType from './conditionFilter/JobType.vue'
-import expType from './conditionFilter/expType.vue'
-import payScope from './conditionFilter/payScope.vue'
-import educationType from './conditionFilter/educationType.vue'
-import scale from './conditionFilter/scale.vue'
-import financingStatus from './conditionFilter/financingStatus.vue'
-import { ref } from 'vue'
+import { useRoute } from 'vue-router'; const route = useRoute()
+import { watch, ref, shallowRef } from 'vue'
+import { filterList, getItemObj } from './dict'
 defineOptions({name: 'retrieval-components-conditionFilter'})
 defineOptions({name: 'retrieval-components-conditionFilter'})
-const emits = defineEmits(['change'])
-const ids = ref({
-  industryIds: 'industryIds',
-  positionId: 'positionId',
-  jobType: 'jobType',
-  expType: 'expType',
-  payType: 'payType',
-  eduType: 'eduType',
-  scale: 'scale',
-  financingStatus: 'financingStatus',
+const emits = defineEmits(['change', 'reset'])
+const props = defineProps({
+  showFilterList: { // 职位检索
+    type: [Array, String],
+    default: 'all'
+  },
 })
 })
+const list = shallowRef(props.showFilterList === 'all' ? filterList : props.showFilterList.map(e => {
+  const item = filterList.find(i => i.key === e.key)
+  return item ? { ...item, ...e } : ''
+}).filter(Boolean))
 
 
-const inputChange = (key, arr) => {
-  const str = arr.length ? arr.join('_') : ''
+const inputChange = ({ idName: key, values }) => {
+  const str = values.length ? values.join('_') : ''
+  if (!key) return
   emits('change', key, str)
   emits('change', key, str)
 }
 }
 
 
-// const clearFun = () => {
-//   Object.keys(ids).forEach(key => emits('change', key, ''))
-// }
+const deleteChip = (item, val) => { // 删除某个标签-更新路由 val结构:{ text:, value: }
+  const ids = item?.checkedItems?.reduce((newArr, e) => {
+    if (e.value !== val.value) newArr.push(e.value)
+    return newArr
+  }, []) || []
+  inputChange({ idName: item.key, values: ids, isDel: true })
+}
 
 
-defineExpose({
-  ids
-})
-</script>
+const showSelectList = ref([])
+// 添加
+const assembleList = ({ key, idsStr }) => {
+  const ids = idsStr.split('_') || []
+  const obj = getItemObj({ key, ids })
+  if (!obj) return
+  const index = showSelectList.value.findIndex(i => i.key === key)
+  if (index === -1) showSelectList.value.push(obj)
+  else showSelectList.value[index] = obj
+}
+
+watch(
+  () => route.query, 
+  (newVal = {}, oldVal = {}) => {
+    // console.log('oldVal', oldVal); console.log('newVal', newVal)
+    // 回显已选筛选-标签
+    if (!Object.keys(newVal).length) { // 路由没有参数
+      showSelectList.value = []
+    }
+    else if (!Object.keys(oldVal).length && Object.keys(newVal).length) { // 只有新加参数
+      Object.keys(newVal).forEach(key => {
+        assembleList({ key, idsStr: newVal[key] })
+      })
+    }
+    else if (Object.keys(newVal).length && Object.keys(oldVal).length) { //路由参数更新
+      Object.keys(newVal).forEach(key => {
+        if (newVal[key] === oldVal[key]) return
+        assembleList({ key, idsStr: newVal[key] })
+      })
+      // showSelectList去掉newVal里面没有的key
+      showSelectList.value = showSelectList.value.filter(item => Object.keys(newVal).includes(item.key))
+    }
+  },
+  { immediate: true },
+  { deep: true }
+)
+</script>
+
+<style lang="scss" scoped>
+.clearFilterCriteria {
+  cursor: pointer;
+  line-height: 28px;
+  margin-right: 12px;
+  font-size: 14px;
+  margin-top: 4px;
+  color: var(--color-999);
+  text-align: right;
+  &:hover {
+    color: var(--v-primary-base);
+  }
+}
+</style>

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

@@ -0,0 +1,110 @@
+<template>
+  <commonStyle :btnTitle="btnTitle" :close-on-content-click="props.isSingle" v-if="show">
+    <!-- 行业类型 -->
+    <industryTypeCard v-if="idName === 'industryIds'" :select="selectedItems" @handleClickIndustry="handle"></industryTypeCard>
+    <!-- 职位类型 -->
+    <jobTypeCard v-else-if="idName === 'positionId'" :select="selectedItems" isSingle echo clearable @handleJobClick="handle"></jobTypeCard>
+    <v-list v-else>
+      <v-list-item
+        v-for="item in items" :key="item.id" :value="item.value"
+        :active="selectedItems.includes(item.value)"
+        color="primary"
+        @click="handle(item.value)"
+      >
+        <template v-if="selectedItems.includes(item.value)" v-slot:append>
+          <v-icon icon="mdi-check"></v-icon>
+        </template>
+        <v-list-item-title>{{ item.label }}</v-list-item-title>
+      </v-list-item>
+    </v-list>
+  </commonStyle>
+</template>
+<script setup>
+import commonStyle from './commonStyle.vue'
+import industryTypeCard from '@/components/industryTypeCard'
+import jobTypeCard from '@/components/jobTypeCard'
+import { getDict } from '@/hooks/web/useDictionaries'
+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,
+    default: false
+  },
+  isSingle: { // 是否单选
+    type: Boolean,
+    default: false
+  },
+  idName: {
+    type: String,
+    default: ''
+  },
+  dictName: {
+    type: [String, Number],
+    default: ''
+  },
+  title: {
+    type: String,
+    default: '类型名称'
+  },
+})
+
+const btnTitle = computed(() => {
+  return selectedItems.value?.length ? `${props.title}(${selectedItems.value.length})` : props.title
+})
+
+let show = ref(false)
+let items = ref()
+const selectedItems = ref([])
+
+const handle = (value) => {
+  if (props.dictName === 1) { // 插槽使用会返回数组
+    selectedItems.value = value
+  } else {
+    if (selectedItems.value.includes(value)) {
+      selectedItems.value = selectedItems.value.filter(i => i !== value)
+    } else {
+      if (props.idName === 'expType') selectedItems.value = value === '2' ? [value] : (selectedItems.value.push(value) && selectedItems.value.filter(i => i !== '2')) // 选中经验不限时清空其它选中项,选中其他的咬去掉经验不限
+      else if (props.isSingle) selectedItems.value = [value] // 单选
+      else selectedItems.value.push(value)
+    }
+  }
+  emits('inputChange', {
+    idName: props.idName,
+    // title: props.title,
+    values: selectedItems.value,
+    isEmit: props.positionIndexPage
+  })
+}
+
+// 字典
+if (props.dictName === 1) show.value = true // 插槽使用
+else if (props.dictName) {
+  getDict(props.dictName).then(({ data }) => {
+    data = data?.length && data || []
+    items.value = data
+    show.value = true
+  })
+} else console.log('error->字典参数未传递!!')
+
+import { useRoute } from 'vue-router'; const route = useRoute()
+watch(
+  () => route.query, 
+  (newVal, oldVal) => {
+    if (!newVal) selectedItems.value = []
+    const str = newVal[props.idName]?.split(',')[0]
+    const arr = str ? str.split('_') : []
+    selectedItems.value = arr?.length? arr : []
+    if (!oldVal) { // 仅刷新时触发
+      if (props.isRefreshEmit) emits('inputChange', { values: selectedItems.value, isEmit: true })
+    }
+  },
+  { immediate: true },
+  { deep: true }
+)
+</script>
+<style lang="scss" scoped>
+</style>

+ 112 - 0
src/views/recruit/personal/position/components/dict.js

@@ -0,0 +1,112 @@
+import { getDict } from '@/hooks/web/useDictionaries'
+import commonPath from './conditionFilter/commonPath.vue'
+
+// dictUse: 查数据回显(一维数组字典),dictShow: 用户操作的字典(用户看到的数据)
+// 当type是tree类型的数据的时候需要提供dictType
+const dictList = [
+  { 
+    dictUse: 'menduner_industry_type',
+    apiType: 'industryList',
+    dictShow: 1,
+    key: 'industryIds',
+    itemKey: 'id',
+    itemText: 'nameCn',
+    title: '行业类型',
+    isSingle: true,
+    path: commonPath,
+    data: []
+  },
+  { 
+    dictUse: 'positionData',
+    apiType: 'positionData',
+    dictShow: 1,
+    key: 'positionId',
+    itemKey: 'id',
+    itemText: 'nameCn',
+    title: '职位类型',
+    isSingle: true,
+    path: commonPath,
+    data: []
+  },
+  { 
+    dictShow: 'menduner_job_type',
+    key: 'jobType',
+    itemKey: 'value',
+    itemText: 'label',
+    title: '求职类型',
+    path: commonPath,
+    data: []
+  },
+  { 
+    dictShow: 'menduner_exp_type',
+    key: 'expType',
+    itemKey: 'value',
+    itemText: 'label',
+    title: '工作经验',
+    path: commonPath,
+    data: []
+  },
+  { 
+    dictShow: 'menduner_pay_scope',
+    key: 'payType',
+    itemKey: 'value',
+    itemText: 'label',
+    title: '薪资待遇',
+    isSingle: true,
+    path: commonPath,
+    data: []
+  },
+  { 
+    dictShow: 'menduner_education_type',
+    key: 'eduType',
+    itemKey: 'value',
+    itemText: 'label',
+    title: '学历要求',
+    path: commonPath,
+    data: []
+  },
+  { 
+    dictShow: 'menduner_scale',
+    key: 'scale',
+    itemKey: 'value',
+    itemText: 'label',
+    title: '公司规模',
+    path: commonPath,
+    data: []
+  },
+  { 
+    dictShow: 'menduner_financing_status',
+    key: 'financingStatus',
+    itemKey: 'value',
+    itemText: 'label',
+    title: '融资阶段',
+    path: commonPath,
+    data: []
+  },
+]
+export const filterList = dictList
+
+// 字典
+const getDictList = async () => {
+  dictList.forEach(async (dictListItem) => {
+    const dictUse = dictListItem.dictUse || dictListItem.dictShow
+    const { data } = await getDict(dictUse, dictListItem.params, dictListItem.apiType)
+    dictListItem.data = data
+  })
+}
+
+const getData = async () => {
+  await getDictList()
+}
+getData()
+
+export const getItemObj = ({ key, ids = [] }) => {
+  const item = dictList.find(dictListItem => dictListItem.key === key)
+  const checkedItems = ids.map(id => {
+    if (!item?.data?.length) return ''
+    const obj = item.data.find(e => e[item.itemKey] === id)
+    return obj ? { text: obj[item.itemText], value: obj[item.itemKey] } : ''
+  }).filter(Boolean)
+
+  return checkedItems?.length ? { key, title: item.title, checkedItems, ref: item.ref } : ''
+}

+ 19 - 36
src/views/recruit/personal/position/index.vue

@@ -2,7 +2,7 @@
 <template>
 <template>
   <div class="default-width">
   <div class="default-width">
     <div class="py-3" style="z-index: 998; background-color: #fff">
     <div class="py-3" style="z-index: 998; background-color: #fff">
-      <div class="stickyBox">
+      <div class="stickyBox mb-5">
         <headSearch
         <headSearch
           tipsText="职位/公司关键字搜索"
           tipsText="职位/公司关键字搜索"
           v-model="headSearchText"
           v-model="headSearchText"
@@ -10,9 +10,8 @@
         ></headSearch>
         ></headSearch>
       </div>
       </div>
       <cityFilter class="mx-5 mb-3" ref="cityFilterRef" @change="handleQueryChange"></cityFilter>
       <cityFilter class="mx-5 mb-3" ref="cityFilterRef" @change="handleQueryChange"></cityFilter>
-      <div v-if="showConditionFilter" class="d-flex justify-space-between mx-5 mb-3">
-        <conditionFilter ref="conditionFilterRef" @change="handleQueryChange"></conditionFilter>
-        <span class="clearFilterCriteria" @click="handleClearConditionFilter">{{ $t('common.clearFilterCriteria') }}</span>
+      <div class="d-flex justify-space-between mx-5 mb-3">
+        <conditionFilter ref="conditionFilterRef" @reset="handleReset" @change="handleQueryChange"></conditionFilter>
       </div>
       </div>
     </div>
     </div>
     <div class="d-flex mt-3">
     <div class="d-flex mt-3">
@@ -41,23 +40,20 @@ import PositionLongStrip from '@/components/PositionLongStrip/item.vue'
 import Empty from '@/components/Empty'
 import Empty from '@/components/Empty'
 import { getJobAdvertisedSearch } from '@/api/position'
 import { getJobAdvertisedSearch } from '@/api/position'
 import CtPagination from '@/components/CtPagination'
 import CtPagination from '@/components/CtPagination'
-import { provide, reactive, ref } from 'vue'
+import { reactive, ref } from 'vue'
 import { dealDictObjData } from '@/utils/position'
 import { dealDictObjData } from '@/utils/position'
 
 
 import { useRoute, useRouter } from 'vue-router'
 import { useRoute, useRouter } from 'vue-router'
 defineOptions({name: 'retrieval-position-page'})
 defineOptions({name: 'retrieval-position-page'})
 const route = useRoute(); const router = useRouter()
 const route = useRoute(); const router = useRouter()
-const cityFilterRef = ref(); const conditionFilterRef = ref()
+const cityFilterRef = ref()
+const conditionFilterRef = ref()
 
 
-const showConditionFilter = ref(true)
 const pageInfo = { pageNo: 1, pageSize: 20}
 const pageInfo = { pageNo: 1, pageSize: 20}
 const items = ref([])
 const items = ref([])
 const total = ref(0)
 const total = ref(0)
 let routeQuery = (route?.query && route.query && Object.keys(route?.query).length) ? reactive(route.query) : reactive({})
 let routeQuery = (route?.query && route.query && Object.keys(route?.query).length) ? reactive(route.query) : reactive({})
-// if (routeQuery && Object.keys(routeQuery).length) {
-//   if ()
-// }
-provide('routeQuery', routeQuery)
+// provide('routeQuery', routeQuery)
 
 
 const headSearchText = ref(routeQuery?.content || '')
 const headSearchText = ref(routeQuery?.content || '')
 
 
@@ -96,17 +92,21 @@ const getData = async () => {
   })
   })
   total.value = number
   total.value = number
 }
 }
+getData() // 不使用cityFilter情况下
 
 
 // 页面刷新
 // 页面刷新
 if (routeQuery && Object.keys(routeQuery).length) getData() 
 if (routeQuery && Object.keys(routeQuery).length) getData() 
 
 
 // 参数改变后刷新路由,触发数据刷新
 // 参数改变后刷新路由,触发数据刷新
 const updateRouter = () => {
 const updateRouter = () => {
-  const str = Object.keys(routeQuery).length ? Object.keys(routeQuery).reduce((res, _key) => {
-    if (routeQuery[_key] !== '') res += `${res ? '&' : ''}${_key}=${routeQuery[_key]}`
-    return res
-  }, '') : ''
-  router.push(`${route.path}?${str}`)
+  let query = {}
+  if (Object.keys(routeQuery).length) {
+    query = Object.keys(routeQuery).reduce((obj, key) => {
+      if (routeQuery[key] !== '') obj[key] = routeQuery[key]
+        return obj
+    }, {})
+  }
+  router.push({ path: route.path, query })
   pageInfo.pageNo = 1
   pageInfo.pageNo = 1
   getData()
   getData()
 }
 }
@@ -118,16 +118,9 @@ const handleQueryChange = (key, val) => { // val为字符串,数组的话用_下
 }
 }
 
 
 // 清空筛选条件
 // 清空筛选条件
-const handleClearConditionFilter = async () => {
-  if (conditionFilterRef.value?.ids) {
-    showConditionFilter.value = false
-    const ids = conditionFilterRef.value.ids
-    Object.keys(ids).forEach(key => {
-      if (routeQuery[key]) delete routeQuery[key]
-    })
-    await updateRouter()
-    showConditionFilter.value = true
-  }
+const handleReset = async () => {
+  routeQuery = {}
+  await updateRouter()
 }
 }
 
 
 // 分页
 // 分页
@@ -138,14 +131,4 @@ const handleChangePage = (index) => {
 </script>
 </script>
 
 
 <style lang="scss" scoped>
 <style lang="scss" scoped>
-.clearFilterCriteria {
-  cursor: pointer;
-  line-height: 28px;
-  margin-right: 12px;
-  font-size: 14px;
-  color: var(--color-999);
-  &:hover {
-    color: var(--v-primary-base);
-  }
-}
 </style>
 </style>