Forráskód Böngészése

找人-根据条件筛选

Xiao_123 2 hónapja
szülő
commit
f4cd0da2f3

+ 14 - 0
api/search.js

@@ -25,4 +25,18 @@ export const getPersonRecommendPage = (params) => {
 			auth: false
 		}
 	})
+}
+
+// 根据条件搜索人才
+export const getPersonConditionSearchPage = (params) => {
+	return request({
+		url: '/app-api/menduner/system/recruit/person-search/page',
+		method: 'GET',
+		params,
+		custom: {
+			openEncryption: true,
+			showLoading: false,
+			auth: false
+		}
+	})
 }

+ 152 - 0
pages/index/components/condition.vue

@@ -0,0 +1,152 @@
+<template>
+	<view class="box defaultBgc">
+		<view style="background-color: #fff;">
+			<uni-search-bar
+        v-model="params.content"
+        radius="5"
+        placeholder="输入关键字"
+        cancelButton="none"
+        :focus="false"
+				@clear="handleSearch('content', null)"
+        @confirm="handleSearch('content', params.content)"
+      ></uni-search-bar>
+			<FilterList :list="filterList" idValue="label" @change="handleSearch"></FilterList>
+		</view>
+		
+		<scroll-view class="scrollBox" :scroll-y="true" @scrolltolower="loadingMore" style="position:relative;">
+			<TalentItem v-if="items?.length" :items="items" :showLastWorkExp="false" />
+			<uni-load-more v-if="items.length" :status="more" />
+			<view v-else class="noJobId">请选择条件搜索人才</view>
+		</scroll-view>
+	</view>
+</template>
+
+<script setup>
+import { ref, nextTick } from 'vue'
+import TalentItem from './talentItem.vue'
+import FilterList from '@/components/FilterList'
+import { getPersonConditionSearchPage } from '@/api/search'
+import { dealDictArrayData } from '@/utils/position'
+import { timesTampChange, getTimeDifferenceInChinese } from '@/utils/date'
+
+const query = ref({
+	pageNo: 1,
+	pageSize: 10
+})
+const params = ref({
+  content: null,
+  positionIds: [],
+  areaIds: [],
+  expType: '',
+  eduType: ''
+})
+const items = ref([])
+const more = ref('more')
+const total = ref(0)
+const filterList = ref([
+  { label: '职位', dictType: 'positionTreeData',key: 'positionId', map: { text: 'nameCn', value: 'id' } },
+  { label: '城市', multiple: true, dictType: 'areaTreeData', key: 'areaIds', map: { text: 'name', value: 'id' } },
+  { label: '最高学历', dictType: 'menduner_education_type', key: 'eduType' },
+  { label: '工作经验', dictType: 'menduner_exp_type', key: 'expType' },
+])
+
+// 根据条件搜索人才
+const getTalentList = async () => {
+  try {
+		more.value = 'loading'
+		const { data } = await getPersonConditionSearchPage(Object.assign(query.value, params.value))
+
+		if (!data.list.length) {
+			more.value = 'noMore'
+			items.value = []
+			total.value = 0
+			return
+		}
+
+		const list = dealDictArrayData([], data.list).map(e => {
+      if (e.workList?.length) {
+        e.workList.forEach(exp => {
+          exp.startTimeStr = exp.startTime ? timesTampChange(exp.startTime, 'Y-M') : '未填写工作时间'
+          exp.endTimeStr = exp.startTime ? exp.endTime ? timesTampChange(exp.endTime, 'Y-M') : '至今' : ''
+          exp.year = exp.endTimeStr ? getTimeDifferenceInChinese(exp.startTime, exp.endTime) : ''
+        })
+      }
+      return e
+    })
+		items.value = items.value.concat(list)
+		total.value = data.total
+
+		if (items.value.length === +data.total) {
+      more.value = 'noMore'
+      return
+    }
+	} catch {
+		query.value.pageNo--
+		more.value = 'more'
+	}
+}
+
+const checkValue = (obj) => {
+  return Object.values(obj).some(value => {  
+    return value !== null && value !== undefined && value !== '' && (Array.isArray(value) ? value.length > 0 : true)
+  })
+}
+
+const handleSearch = (key, value) => {
+	query.value.pageNo = 1
+	params.value[key] = value
+
+	if (!checkValue(params.value)) {
+		uni.showToast({
+			title: '请至少选择一个查询条件',
+			icon: 'none',
+			duration: 2000
+		})
+		more.value = 'noMore'
+		items.value = []
+		total.value = 0
+		return
+	}
+
+	items.value = []
+	total.value = 0
+	getTalentList()
+}
+
+// 加载更多
+const loadingMore = () => {
+	if (more.value === 'noMore') return
+
+  more.value = 'loading'
+  query.value.pageNo++
+	getTalentList()
+}
+</script>
+
+<style scoped lang="scss">
+.noJobId {
+	text-align: center;
+	line-height: 60vh;
+	color: #666;
+}
+.box {
+  height: 100vh;
+  overflow: hidden;
+  box-sizing: border-box;
+  display: flex;
+  flex-direction: column;
+}
+.scrollBox{
+	flex: 1;
+  padding-bottom: 100px;
+  box-sizing: border-box;
+	height: 0 !important;
+}
+.stickFilter {
+  z-index: 1;
+  position: sticky;
+  box-shadow: 0px 10rpx 12rpx 0px rgba(195, 195, 195, .25);
+  top: 110rpx;
+	background-color: #fff;
+}
+</style>

+ 16 - 23
pages/index/components/recommend.vue

@@ -1,6 +1,6 @@
 <template>
-	<view>
-		<view style="padding: 0 15px;">
+	<view class="box defaultBgc">
+		<view style="padding: 10px 15px 15px 15px; background-color: #fff;">
 			<uni-data-select 
 				v-model="query.jobId" 
 				:clear="false" 
@@ -10,7 +10,7 @@
 			></uni-data-select>
 		</view>
 		
-		<scroll-view class="scrollBox defaultBgc" :scroll-y="true" :scroll-top="scrollTop" @scrolltolower="loadingMore" @scroll="onScroll" style="position:relative;">
+		<scroll-view class="scrollBox" :scroll-y="true" @scrolltolower="loadingMore" style="position:relative;">
 			<TalentItem v-if="query.jobId && (items?.length || more !== 'loading')" :items="items" />
 			<uni-load-more v-if="query.jobId" :status="more" />
 			<view v-else class="noJobId">请选择要推荐人才的职位</view>
@@ -32,10 +32,6 @@ const query = ref({
 	jobId: null
 })
 const items = ref([])
-const scrollTop = ref(0)
-const old = ref({
-  scrollTop: 0
-})
 const more = ref('more')
 const total = ref(0)
 
@@ -44,21 +40,13 @@ const getRecommendList = async () => {
   try {
 		more.value = 'loading'
 		const { data } = await getPersonRecommendPage(query.value)
-		scrollTop.value = old.value.scrollTop
-    nextTick(() => {
-      scrollTop.value = 0
-    })
 
 		if (!data.list.length) {
 			more.value = 'noMore'
 			return
 		}
 
-		const list = dealDictArrayData([], data.list).map(e => {
-      e.areaName = e.area?.str ?? ''
-      e.regName = e.reg?.str ?? ''
-      return e
-    })
+		const list = dealDictArrayData([], data.list)
 		items.value = items.value.concat(list)
 		total.value = data.total
 
@@ -76,19 +64,17 @@ const getRecommendList = async () => {
 const handleChangeJob = (e) => {
 	query.value.pageNo = 1
 	items.value = []
+	total.value = 0
 	if (e) getRecommendList()
 }
 
 // 加载更多
 const loadingMore = () => {
+	if (more.value === 'noMore') return
   more.value = 'loading'
   query.value.pageNo++
 	getRecommendList()
 }
-
-const onScroll = (e) =>{
-  old.value.scrollTop = e.detail.scrollTop
-}
 </script>
 
 <style scoped lang="scss">
@@ -97,10 +83,17 @@ const onScroll = (e) =>{
 	line-height: 60vh;
 	color: #666;
 }
+.box {
+  height: 100vh;
+  overflow: hidden;
+  box-sizing: border-box;
+  display: flex;
+  flex-direction: column;
+}
 .scrollBox{
-  padding-bottom: 24rpx;
+	flex: 1;
+  padding-bottom: 100px;
   box-sizing: border-box;
-	margin-top: 10px;
-	min-height: 80vh;
+	height: 0 !important;
 }
 </style>

+ 24 - 3
pages/index/components/talentItem.vue

@@ -18,18 +18,20 @@
 					</view>
 				</view>
 			</view>
+			
 			<!-- 感兴趣职位 -->
-			<view v-if="val.position?.positionNames" class="ss-m-t-15 color-666">
+			<view v-if="val.position?.positionNames && showLastWorkExp" class="ss-m-t-15 color-666">
 				感兴趣职位:{{ val.position?.positionNames || '暂无' }}
 			</view>
+
 			<!-- 最近一份工作经验 -->
 			<view v-if="val?.lastWorkExp && showLastWorkExp" class="ss-m-t-15 color-666">
 				<view>
 					<uni-icons type="smallcircle" class="ss-m-r-10" color="#666"></uni-icons>
-					{{ val.lastWorkExp.enterpriseName }}
+					{{ formatName(val.lastWorkExp.enterpriseName) || '未填写企业名称' }}
 				</view>
 				<view class="ss-m-l-45">
-					<span>{{ val.lastWorkExp.positionName }}</span>
+					<span>{{ formatName(val.lastWorkExp.positionName) || '未填写职位名称' }}</span>
 					<span v-if="val.lastWorkExp.startTime" class="ss-m-l-20">
 						{{ val.lastWorkExp.startTime ? timesTampChange(val.lastWorkExp.startTime, 'Y-M') : '暂无' }}
 						- 
@@ -37,6 +39,24 @@
 					</span>
 				</view>
 			</view>
+
+			<!-- 工作经历 -->
+			<view v-if="val?.workList && val.workList.length > 0 && !showLastWorkExp" class="ss-m-t-15 color-666">
+				<view v-for="k in val.workList" :key="k.id" class="ss-m-b-30">
+					<view>
+						<uni-icons type="smallcircle" class="ss-m-r-10" color="#666"></uni-icons>
+						{{ formatName(k.enterpriseName) || '未填写企业名称' }}
+					</view>
+					<view class="ss-m-l-45">
+						<span>{{ formatName(k.positionName) || '未填写职位名称' }}</span>
+						<span class="ss-m-l-20">
+							<span>{{ k.startTimeStr }}</span>
+							<span v-if="k.endTimeStr"> - {{ k.endTimeStr }}</span>
+							<span v-if="k.year"> ({{ k.year }})</span>
+						</span>
+					</view>
+				</view>
+			</view>
 		</uni-card>
 	</view>
 </template>
@@ -44,6 +64,7 @@
 <script setup>
 import { getUserAvatar } from '@/utils/avatar.js'
 import { timesTampChange } from '@/utils/date'
+import { formatName } from '@/utils/getText'
 
 defineProps({
 	items: Array,

+ 6 - 6
pages/index/search.vue

@@ -1,7 +1,7 @@
 <template>
 	<layout-page>
 		<view >
-      <view style="padding: 0 15px 15px 15px;">
+      <view style="padding: 0 15px 0 15px;">
 				<uni-segmented-control 
 					:current="current"
 					:values="tabList"
@@ -12,11 +12,10 @@
 			</view>
 
 			<!-- 职位推荐 -->
-			<view v-if="current === 0">
-				<RecommendPage :jobList="jobList" />
-			</view>
+			<RecommendPage v-if="current === 0" :jobList="jobList" />
+
 			<!-- 条件筛选 -->
-			<view v-else>条件</view>
+			<ConditionPage v-else />
 
     </view>
 	</layout-page>
@@ -32,8 +31,9 @@ import { getJobAdvertised } from '@/api/search'
 import { dealDictArrayData } from '@/utils/position'
 import { formatName } from '@/utils/getText'
 import RecommendPage from './components/recommend.vue'
+import ConditionPage from './components/condition.vue'
 
-const current = ref(0)
+const current = ref(1)
 const tabList = ['职位推荐', '条件筛选']
 
 // 职位列表

+ 2 - 3
pagesB/personnelDetails/components/baseInfo.vue

@@ -26,15 +26,14 @@
         :text="tag"
         inverted="false"
         size="medium"
-        custom-style="background-color: #ececec; color: #666; border-color: #ececec; display: inline-block;"
+        custom-style="background-color: #ececec; color: #666; border-color: #ececec; display: inline-block;margin-bottom: 10px;"
       />
     </view>
 	</view>
 </template>
 
 <script setup>
-const props = defineProps({ data: Object })
-console.log(props.data, 'props.data')
+defineProps({ data: Object })
 
 const desc = ['jobStatusName', 'eduName', 'expName']
 </script>

+ 9 - 2
pagesB/personnelDetails/components/jobIntention.vue

@@ -2,12 +2,19 @@
 	<view>
 		<view v-for="val in data" :key="val.id" class="ss-m-b-40">
 			<view class="item">
-        <!-- <view class="item-title">{{ val.jobTypeName}}</view> -->
         <view style="display: flex;justify-content: space-between;align-items: center;">
           <text class="mr-20 item-title">{{ val.position}}</text>
           <text class="color-error font-weight-bold">{{ val.payFrom }} {{ val.payFrom  && val.payTo ? '-' : ''}} {{ val.payTo}}</text>
         </view>
-        <view>{{ val.interestedArea && val.interestedArea.length ? val.workArea + ',' + val.interestedArea.map(e => e.name).join(',') : val.workArea }}</view>
+        <view>
+          {{ 
+            val.interestedArea && val.interestedArea.length 
+            ? 
+            (val.workArea ? val.workArea + ',' : '') + val.interestedArea.map(e => e.name).join(',') 
+            : 
+            (val.workArea || '')
+          }}
+        </view>
         <view class="item-tags">
           <view v-for="industry in val.industry" :key="industry.id" class="tag">{{ industry.nameCn }}</view>
         </view>

+ 53 - 0
utils/date.js

@@ -36,4 +36,57 @@ export const convertYearMonthToTimestamp = (yearMonth) => {
   const date = new Date(Date.UTC(year, month - 1, 1))
   const timestamp = date.getTime()
   return timestamp  
+}
+
+export const getTimeDifferenceInChinese = (startTime, endTime) => {
+  // 将时间戳转换为 Date 对象(假设时间戳单位为毫秒)
+  const startDate = startTime ? new Date(startTime) : new Date();
+  const endDate = endTime ? new Date(endTime) : new Date();
+
+  // 计算年份差
+  let yearsDiff = endDate.getFullYear() - startDate.getFullYear();
+  // 计算月份差(考虑年份差后调整)
+  let monthsDiff = endDate.getMonth() - startDate.getMonth();
+  // 如果月份差为负,则从上一年借月
+  if (monthsDiff < 0) {
+    yearsDiff--;
+    monthsDiff += 12;
+  }
+  // 计算日期差(考虑月份差后调整,如果日期差为负,则从上一月借天)
+  let daysDiff = endDate.getDate() - startDate.getDate();
+  if (daysDiff < 0) {
+    monthsDiff--;
+    // 获取 startDate 所在月的最后一天
+    const lastDayOfMonth = new Date(startDate.getFullYear(), startDate.getMonth() + 1, 0).getDate();
+    daysDiff += lastDayOfMonth; // 加上最后一天以补全月份差
+  }
+
+  // 构建结果字符串
+  let result = "";
+  if (yearsDiff > 0) {
+    result += `${yearsDiff}年`;
+  }
+  if (monthsDiff > 0) {
+    if (result) {
+      // 如果已经有年份差异,则直接添加月份数(不带单位),后面正则替换会处理
+      result += monthsDiff;
+    } else {
+      // 如果没有年份差异,则正常添加月份和单位
+      result += `${monthsDiff}个月`;
+      // 特别处理只有1个月的情况
+      if (monthsDiff === 1) {
+        result = "不到1个月"; // 直接替换为“不到1个月”,避免后续复杂的正则替换
+      }
+    }
+  } else if (!result && daysDiff >= 0) {
+    // 如果没有年份和月份差异,但天数差异存在(这里其实没处理天数,只是为完整性添加)
+    // 理论上应该处理天数,但题目要求只到月份,所以这里直接返回“不到1个月”
+    result = "不到1个月";
+  }
+ 
+  // 如果之前添加了月份数但没有年份(且不是直接处理的1个月情况),则需要去除末尾多余的数字并添加“个月”
+  if (result && !/\d年$/.test(result) && /\d$/.test(result)) {
+    result += "个月";
+  }
+  return result
 }