Files
ks-app-employment-service/pages/nearby/components/one.vue
2025-12-05 13:31:15 +08:00

482 lines
14 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="nearby-container">
<!-- 地图区域 - 可折叠 -->
<view
class="nearby-map"
@touchmove.stop.prevent
:class="{ 'map-collapsed': isMapCollapsed }"
>
<map
style="width: 100%; height: 400px"
:latitude="latitudeVal"
:longitude="longitudeVal"
:markers="mapCovers"
:circles="mapCircles"
:controls="mapControls"
@controltap="handleControl"
></map>
<view class="nearby-select">
<view class="select-view" @click="changeRangeShow">
<text>{{ pageState.search.radius }}km</text>
<image class="view-sx" :class="{ active: rangeShow }" src="@/static/icon/shaixun.png"></image>
</view>
<transition name="fade-slide">
<view class="select-list" v-if="rangeShow">
<view class="list-item button-click" v-for="(item, index) in range" @click="changeRadius(item)">
{{ item }}km
</view>
</view>
</transition>
</view>
</view>
<!-- 筛选条件 - 固定显示 -->
<view class="nav-filter" @touchmove.stop.prevent>
<view class="filter-top">
<scroll-view :scroll-x="true" :show-scrollbar="false" class="tab-scroll">
<view class="jobs-left">
<view
class="job button-click"
:class="{ active: state.tabIndex === 'all' }"
@click="choosePosition('all')"
>
全部
</view>
<view
class="job button-click"
:class="{ active: state.tabIndex === index }"
v-for="(item, index) in userInfo.jobTitle"
:key="index"
@click="choosePosition(index)"
>
{{ item }}
</view>
</view>
</scroll-view>
<view class="jobs-add button-click" @click="navTo('/packageA/pages/addPosition/addPosition')">
<uni-icons class="iconsearch" color="#666D7F" type="plusempty" size="18"></uni-icons>
<text>添加</text>
</view>
</view>
<view class="filter-bottom">
<view class="btm-left">
<view
class="button-click filterbtm"
:class="{ active: pageState.search.order === item.value }"
v-for="item in rangeOptions"
@click="handelHostestSearch(item)"
:key="item.value"
>
{{ item.text }}
</view>
</view>
<view class="btm-right button-click" @click="openFilter">
筛选
<image class="right-sx" :class="{ active: showFilter }" src="@/static/icon/shaixun.png"></image>
</view>
</view>
</view>
<!-- 列表滚动区域 -->
<scroll-view
:scroll-y="true"
class="nearby-scroll"
@scrolltolower="scrollBottom"
lower-threshold="50"
ref="scrollViewRef"
@scroll="handleScroll"
>
<view class="nearby-list">
<view class="one-cards">
<renderJobs
v-if="list.length"
:list="list"
:longitude="longitudeVal"
:latitude="latitudeVal"
></renderJobs>
<empty v-else pdTop="60"></empty>
<loadmore ref="loadmoreRef"></loadmore>
</view>
</view>
</scroll-view>
<!-- 筛选 -->
<select-filter ref="selectFilterModel"></select-filter>
</view>
</template>
<script setup>
import { reactive, inject, watch, ref, onMounted, onBeforeUnmount } from 'vue';
import { onLoad, onShow } from '@dcloudio/uni-app';
const { $api, navTo, debounce, customSystem } = inject('globalFunction');
import { storeToRefs } from 'pinia';
import useLocationStore from '@/stores/useLocationStore';
import useUserStore from '@/stores/useUserStore';
const { userInfo } = storeToRefs(useUserStore());
const { longitudeVal, latitudeVal } = storeToRefs(useLocationStore());
import point2 from '@/static/icon/Location1.png';
import LocationPng from '@/static/icon/Location12.png';
import selectFilter from '@/components/selectFilter/selectFilter.vue';
const emit = defineEmits(['onFilter']);
const range = ref([1, 2, 4, 6, 8, 10]);
const rangeShow = ref(false);
const selectFilterModel = ref(null);
const scrollViewRef = ref(null);
const isMapCollapsed = ref(false);
const tMap = ref();
const progress = ref();
const mapCovers = ref([]);
const mapCircles = ref([]);
const mapControls = ref([
{
id: 1,
position: {
// 控件位置
left: customSystem.systemInfo.screenWidth - 48 - 14,
top: 320,
width: 48,
height: 48,
},
iconPath: LocationPng, // 控件图标
},
]);
const loadmoreRef = ref(null);
const pageState = reactive({
page: 0,
total: 100,
maxPage: 2,
pageSize: 10,
search: {
radius: 1,
order: 0,
},
});
const isLoaded = ref(false);
const showFilter = ref(false);
const list = ref([]);
const state = reactive({
progressWidth: '200px',
});
const rangeOptions = ref([
{ value: 0, text: '推荐' },
{ value: 1, text: '最热' },
{ value: 2, text: '最新发布' },
{ value: 3, text: '疆外' },
]);
function changeRangeShow() {
rangeShow.value = !rangeShow.value;
}
function changeRadius(item) {
pageState.search.radius = item;
rangeShow.value = false;
progressChange(item);
}
function choosePosition(index) {
state.tabIndex = index;
list.value = [];
if (index === 'all') {
pageState.search = {
...pageState.search,
jobTitle: '',
};
getJobList('refresh');
} else {
// const id = useUserStore().userInfo.jobTitleId.split(',')[index];
pageState.search.jobTitle = useUserStore().userInfo.jobTitle[index];
getJobList('refresh');
}
}
function openFilter() {
showFilter.value = true;
selectFilterModel.value?.open({
title: '筛选',
maskClick: true,
success: (values) => {
pageState.search = {
...pageState.search,
};
for (const [key, value] of Object.entries(values)) {
pageState.search[key] = value.join(',');
}
showFilter.value = false;
console.log(pageState.search);
getJobList('refresh');
},
cancel: () => {
showFilter.value = false;
},
});
}
onLoad(() => {});
async function loadData() {
try {
if (isLoaded.value) return;
isLoaded.value = true;
} catch (err) {
isLoaded.value = false; // 重置状态允许重试
throw err;
}
}
function scrollBottom() {
getJobList();
loadmoreRef.value.change('loading');
}
function handleControl(e) {
switch (e.detail.controlId) {
case 1:
getInit();
break;
}
}
onMounted(() => {
// $api.msg('使用模拟定位');
getInit();
});
function getInit() {
useLocationStore()
.getLocation()
.then((res) => {
mapCovers.value = [
{
latitude: res.latitude,
longitude: res.longitude,
iconPath: point2,
},
];
mapCircles.value = [
{
latitude: res.latitude,
longitude: res.longitude,
radius: 1000,
fillColor: '#1c52fa25',
color: '#256BFA',
},
];
getJobList('refresh');
});
}
function progressChange(value) {
const range = 1 + value;
pageState.search.radius = range;
mapCircles.value = [
{
latitude: latitudeVal.value,
longitude: longitudeVal.value,
radius: range * 1000,
fillColor: '#1c52fa25',
color: '#256BFA',
},
];
debounceAjax('refresh');
}
// 处理滚动事件,实现地图折叠
function handleScroll(e) {
const scrollTop = e.detail.scrollTop;
// 当滚动超过100rpx时折叠地图
isMapCollapsed.value = scrollTop > 100;
}
let debounceAjax = debounce(getJobList, 500);
function getJobList(type = 'add') {
if (type === 'add' && pageState.page < pageState.maxPage) {
pageState.page += 1;
}
if (type === 'refresh') {
pageState.page = 1;
pageState.maxPage = 2;
}
let params = {
current: pageState.page,
pageSize: pageState.pageSize,
longitude: longitudeVal.value,
latitude: latitudeVal.value,
...pageState.search,
};
$api.createRequest('/app/job/nearJob', params, 'POST').then((resData) => {
const { rows, total } = resData;
if (type === 'add') {
const str = pageState.pageSize * (pageState.page - 1);
const end = list.value.length;
const reslist = rows;
list.value.splice(str, end, ...reslist);
} else {
list.value = rows;
}
pageState.total = resData.total;
pageState.maxPage = Math.ceil(pageState.total / pageState.pageSize);
if (loadmoreRef.value && typeof loadmoreRef.value.change === 'function') {
if (rows.length < pageState.pageSize) {
loadmoreRef.value.change('noMore');
} else {
loadmoreRef.value.change('more');
}
}
});
}
function handelHostestSearch(val) {
pageState.search.order = val.value;
getJobList('refresh');
}
function handleFilterConfirm(val) {
pageState.search = {
radius: pageState.search.radius,
order: pageState.search.order,
};
for (const [key, value] of Object.entries(val)) {
pageState.search[key] = value.join(',');
}
getJobList('refresh');
}
defineExpose({ loadData, handleFilterConfirm });
</script>
<style lang="stylus" scoped>
.nearby-select{
width: 184rpx;
height: 64rpx;
background: #FFFFFF;
border-radius: 12rpx 12rpx 12rpx 12rpx;
position: absolute
right: 28rpx;
top: 28rpx
.select-view{
display: flex
align-items: center
justify-content: center
height: 100%
.view-sx{
width: 26rpx;
height: 26rpx;
}
.active{
transform: rotate(180deg)
}
}
.select-list{
border-radius: 12rpx 12rpx 12rpx 12rpx;
overflow: hidden
position: absolute
top: 76rpx
left: 0
display: flex
flex-direction: column
background: #FFFFFF;
text-align: center
.list-item{
width: 184rpx;
line-height: 68rpx;
height: 68rpx;
border-radius: 0rpx 0rpx 0rpx 0rpx;
}
}
}
.nearby-container
// 确保容器占据整个可用视口高度,包括安全区域
height: calc(100vh - var(--window-top));
display: flex;
flex-direction: column;
overflow: hidden;
.nearby-map
height: 767rpx;
background: #e8e8e8;
overflow: hidden
transition: height 0.3s ease;
&.map-collapsed {
height: 0;
overflow: hidden;
}
.nearby-scroll
// 使用flex布局让scroll-view自适应高度占据剩余空间
flex: 1;
overflow: hidden;
transition: flex 0.3s ease;
.nearby-list
.one-cards{
display: flex;
flex-direction: column;
padding: 0 20rpx 20rpx 20rpx;
background: #f4f4f4
}
// 筛选条件样式 - 顶级选择器
.nav-filter
padding: 16rpx 28rpx 0 28rpx
background: #ffffff
.filter-top
display: flex
justify-content: space-between;
.tab-scroll
flex: 1;
overflow: hidden;
margin-right: 20rpx
white-space: nowrap;
overflow: hidden;
text-overflow: clip;
-webkit-mask-image: linear-gradient(to right, black 60%, transparent);
mask-image: linear-gradient(to right, black 60%, transparent);
.jobs-left
display: flex
flex-wrap: nowrap
.job
font-weight: 400;
font-size: 36rpx;
color: #666D7F;
margin-right: 32rpx;
white-space: nowrap
.active
font-family: 'PingFangSC-Medium', 'PingFang SC', 'Helvetica Neue', Helvetica, Arial, 'Microsoft YaHei', sans-serif;
font-weight: 500;
font-size: 36rpx;
color: #000000;
.jobs-add
display: flex
align-items: center;
justify-content: center;
font-weight: 400;
font-size: 32rpx;
color: #666D7F;
line-height: 38rpx;
.filter-bottom
display: flex
justify-content: space-between
padding: 24rpx 0
.btm-left
display: flex
.filterbtm
font-weight: 400;
font-size: 32rpx;
color: #666D7F;
margin-right: 40rpx
.active
font-weight: 500;
font-size: 32rpx;
color: #256BFA;
.btm-right
font-weight: 400;
font-size: 32rpx;
color: #6C7282;
.right-sx
width: 26rpx;
height: 26rpx;
.active
transform: rotate(180deg)
</style>