|
|
@@ -6,7 +6,7 @@
|
|
|
<div class="search-wrap">
|
|
|
<location-search
|
|
|
class="search"
|
|
|
- user-location="113.61702297075017,23.584863449735067"
|
|
|
+ :userLocation="userLocation"
|
|
|
@change="handleLocationChange"
|
|
|
/>
|
|
|
</div>
|
|
|
@@ -21,7 +21,7 @@
|
|
|
<template #header>
|
|
|
<div class="header-bar"></div>
|
|
|
<div class="select-group">
|
|
|
- <el-select class="select-item" v-model="paramsPage.distance" placeholder="距离">
|
|
|
+ <el-select class="select-item" v-model="paramsPage.distance" placeholder="距离" @change="resetList">
|
|
|
<el-option
|
|
|
v-for="item in distanceOptions"
|
|
|
:key="item.value"
|
|
|
@@ -29,7 +29,7 @@
|
|
|
:value="item.value"
|
|
|
/>
|
|
|
</el-select>
|
|
|
- <el-select class="select-item" v-model="paramsPage.serviceType" placeholder="服务类型">
|
|
|
+ <el-select class="select-item" v-model="paramsPage.serviceType" placeholder="服务类型" @change="resetList">
|
|
|
<el-option
|
|
|
v-for="item in serviceTypeList"
|
|
|
:key="item.id"
|
|
|
@@ -40,8 +40,18 @@
|
|
|
</div>
|
|
|
</template>
|
|
|
<div class="hall-content">
|
|
|
- <div class="farm-list" ref="cardContentRef" :style="{ height: `${cardContentHeight}px` }">
|
|
|
- <div class="task-item" v-for="item in agriculturalStoreList" :key="item.id" @click="handleDetail(item)">
|
|
|
+ <List
|
|
|
+ v-model:loading="loading"
|
|
|
+ :finished="finished"
|
|
|
+ finished-text="没有更多了"
|
|
|
+ :immediate-check="false"
|
|
|
+ @load="onLoad"
|
|
|
+ class="van-list-container"
|
|
|
+ :style="{ height: `${cardContentHeight}px` }"
|
|
|
+ ref="cardContentRef"
|
|
|
+ >
|
|
|
+ <div class="farm-list">
|
|
|
+ <div class="task-item" v-for="item in agriculturalStoreList" :key="item.id" @click="handleDetail(item)">
|
|
|
<div class="task-content">
|
|
|
<div class="content-t">
|
|
|
<img class="content-img" :src="item.bgUrl || require('@/assets/img/home/nz.png')" alt="" />
|
|
|
@@ -95,21 +105,23 @@
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
+ </List>
|
|
|
</div>
|
|
|
</floating-panel>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
-import { FloatingPanel, Icon } from "vant";
|
|
|
-import { computed, nextTick, onMounted, ref,onActivated } from "vue";
|
|
|
+import { FloatingPanel, Icon, List } from "vant";
|
|
|
+import { computed, nextTick, onMounted, ref, onActivated } from "vue";
|
|
|
import { useStore } from "vuex";
|
|
|
import IndexMap from "../map/index";
|
|
|
import LocationSearch from "@/components/pageComponents/LocationSearch.vue";
|
|
|
import { Point } from "ol/geom";
|
|
|
import Feature from "ol/Feature";
|
|
|
+import * as util from "@/common/ol_common.js";
|
|
|
import { useRouter } from "vue-router";
|
|
|
const router = useRouter();
|
|
|
const props = defineProps({
|
|
|
@@ -132,6 +144,7 @@ const height = ref(anchors.value[0]);
|
|
|
const indexMap = new IndexMap();
|
|
|
const mapContainer = ref(null);
|
|
|
const mapPoint = ref(null);
|
|
|
+const userLocation = localStorage.getItem("MINI_USER_LOCATION");
|
|
|
|
|
|
onMounted(() => {
|
|
|
mapPoint.value = store.state.home.miniUserLocationPoint;
|
|
|
@@ -141,14 +154,35 @@ onMounted(() => {
|
|
|
indexMap.setGardenHallClickCallback((featureData,) => {
|
|
|
router.push(`/expert_homepage?isStore=true&id=${featureData.id}`);
|
|
|
});
|
|
|
+
|
|
|
+ // 添加初始位置图标
|
|
|
+ if (indexMap.clickPointLayer && mapPoint.value) {
|
|
|
+ const initialPoint = util.wktCastGeom(mapPoint.value).getFirstCoordinate();
|
|
|
+ const point = new Feature(new Point(initialPoint));
|
|
|
+ indexMap.clickPointLayer.addFeature(point);
|
|
|
+ }
|
|
|
});
|
|
|
-
|
|
|
- getStoreList();
|
|
|
+ // 手动触发首次加载
|
|
|
+ getStoreList(false);
|
|
|
+ initMap();
|
|
|
});
|
|
|
|
|
|
+const initMap = async () => {
|
|
|
+ const params = {
|
|
|
+ limit: 999,
|
|
|
+ page: 1,
|
|
|
+ type: 1,
|
|
|
+ };
|
|
|
+ const res = await VE_API.farm.getStoreList(params);
|
|
|
+ if(res.data.length > 0){
|
|
|
+ indexMap.initDataHall(res.data);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
onActivated(() => {
|
|
|
if(props.active == 1){
|
|
|
- getStoreList();
|
|
|
+ resetList();
|
|
|
+ initMap();
|
|
|
}
|
|
|
});
|
|
|
|
|
|
@@ -157,20 +191,73 @@ const handleDetail = (item) => {
|
|
|
};
|
|
|
|
|
|
const paramsPage = ref({
|
|
|
- serviceType: "",
|
|
|
- distance:""
|
|
|
+ serviceType: "全部类型",
|
|
|
+ distance:"不限范围"
|
|
|
})
|
|
|
|
|
|
const agriculturalStoreList = ref([]);
|
|
|
-const getStoreList = (isInitial = true) => {
|
|
|
+const loading = ref(false);
|
|
|
+const finished = ref(false);
|
|
|
+const page = ref(1);
|
|
|
+const limit = ref(10); // 每页加载数量
|
|
|
+const resetTimer = ref(null); // 防抖定时器
|
|
|
+const currentRequestKey = ref(null); // 当前请求的唯一标识
|
|
|
+
|
|
|
+// 生成请求的唯一标识
|
|
|
+const generateRequestKey = (params) => {
|
|
|
+ return JSON.stringify({
|
|
|
+ point: params.point,
|
|
|
+ page: params.page,
|
|
|
+ limit: params.limit,
|
|
|
+ type: params.type,
|
|
|
+ serviceType: params.serviceType || '',
|
|
|
+ distance: params.distance || '',
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+// 加载列表数据
|
|
|
+const getStoreList = async (isLoadMore = false) => {
|
|
|
const params = {
|
|
|
- page: 1,
|
|
|
- limit: 99,
|
|
|
+ point: mapPoint.value,
|
|
|
+ page: page.value,
|
|
|
+ limit: limit.value,
|
|
|
type: 1,
|
|
|
+ ...paramsPage.value,
|
|
|
};
|
|
|
- VE_API.farm.getStoreList(params).then((res) => {
|
|
|
- if (res.data.length) {
|
|
|
- agriculturalStoreList.value = res.data.map((item) => {
|
|
|
+
|
|
|
+ // 如果服务类型为空字符串,不传该参数
|
|
|
+ if (params.serviceType === "全部类型") {
|
|
|
+ delete params.serviceType;
|
|
|
+ }
|
|
|
+ if (params.distance === "不限范围") {
|
|
|
+ delete params.distance;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 生成请求的唯一标识
|
|
|
+ const requestKey = generateRequestKey(params);
|
|
|
+
|
|
|
+ // 如果正在加载相同的请求,直接返回
|
|
|
+ if (loading.value && currentRequestKey.value === requestKey) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果正在加载,直接返回(防止不同参数的请求重叠)
|
|
|
+ if (loading.value) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置当前请求标识和加载状态
|
|
|
+ currentRequestKey.value = requestKey;
|
|
|
+ loading.value = true;
|
|
|
+
|
|
|
+ try {
|
|
|
+ const res = await VE_API.farm.getStoreList(params);
|
|
|
+ // 获取总数和当前页数据
|
|
|
+ const totalCount = res.count || 0;
|
|
|
+ const currentData = res.data || [];
|
|
|
+
|
|
|
+ if (currentData.length > 0) {
|
|
|
+ const newList = currentData.map((item) => {
|
|
|
return {
|
|
|
...item,
|
|
|
speciesList: JSON.parse(item.serviceSpecies || "[\"--\"]"),
|
|
|
@@ -178,60 +265,135 @@ const getStoreList = (isInitial = true) => {
|
|
|
serviceTypeList: JSON.parse(item.serviceType || "[\"--\"]"),
|
|
|
};
|
|
|
});
|
|
|
- if (isInitial) {
|
|
|
- indexMap.initDataHall(res.data);
|
|
|
+
|
|
|
+ if (isLoadMore) {
|
|
|
+ // 加载更多时追加数据
|
|
|
+ agriculturalStoreList.value = [...agriculturalStoreList.value, ...newList];
|
|
|
+ } else {
|
|
|
+ // 首次加载或刷新时替换数据
|
|
|
+ agriculturalStoreList.value = newList;
|
|
|
}
|
|
|
+
|
|
|
+ // 使用 count 判断是否还有更多数据
|
|
|
+ // 如果当前已加载的数据量 >= 总数,说明没有更多数据了
|
|
|
+ const loadedCount = agriculturalStoreList.value.length;
|
|
|
+ if (totalCount > 0 && loadedCount >= totalCount) {
|
|
|
+ finished.value = true;
|
|
|
+ } else if (totalCount === 0) {
|
|
|
+ // 如果没有总数信息,使用原来的判断方式
|
|
|
+ if (currentData.length < limit.value) {
|
|
|
+ finished.value = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ } else {
|
|
|
+ finished.value = true;
|
|
|
}
|
|
|
+ } catch (error) {
|
|
|
+ console.error('获取列表失败:', error);
|
|
|
+ finished.value = true;
|
|
|
+ } finally {
|
|
|
+ // 加载完成后,设置 loading = false 告诉 List 组件加载完成
|
|
|
+ loading.value = false;
|
|
|
+ // 清除当前请求标识
|
|
|
+ currentRequestKey.value = null;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 滚动加载更多
|
|
|
+const onLoad = async () => {
|
|
|
+ if (finished.value) return;
|
|
|
+ const isLoadMore = page.value > 1;
|
|
|
+ await getStoreList(isLoadMore);
|
|
|
+ // 加载完成后再增加页码
|
|
|
+ if (!finished.value) {
|
|
|
+ page.value++;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 重置列表(筛选条件变化时调用)
|
|
|
+const resetList = () => {
|
|
|
+ // 清除之前的定时器
|
|
|
+ if (resetTimer.value) {
|
|
|
+ clearTimeout(resetTimer.value);
|
|
|
+ resetTimer.value = null;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 使用防抖,延迟执行,避免快速连续触发
|
|
|
+ resetTimer.value = setTimeout(() => {
|
|
|
+ // 清除当前请求标识,确保可以重新请求
|
|
|
+ currentRequestKey.value = null;
|
|
|
+ page.value = 1;
|
|
|
+ finished.value = false;
|
|
|
+ agriculturalStoreList.value = [];
|
|
|
+ getStoreList(false);
|
|
|
+ resetTimer.value = null;
|
|
|
+ }, 100);
|
|
|
+};
|
|
|
+
|
|
|
+// 更新地图位置和图标
|
|
|
+const updateMapLocation = (coordinateArray) => {
|
|
|
+ if (!indexMap || !indexMap.kmap) return;
|
|
|
+
|
|
|
+ // 更新地图位置
|
|
|
+ indexMap.kmap.getView().animate({
|
|
|
+ center: coordinateArray,
|
|
|
+ zoom: 16,
|
|
|
+ duration: 500,
|
|
|
});
|
|
|
+
|
|
|
+ // 更新点击点图标
|
|
|
+ if (indexMap.clickPointLayer) {
|
|
|
+ indexMap.clickPointLayer.source.clear();
|
|
|
+ const point = new Feature(new Point(coordinateArray));
|
|
|
+ indexMap.clickPointLayer.addFeature(point);
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
// 处理位置搜索变化
|
|
|
function handleLocationChange(data) {
|
|
|
if (data && data.coordinateArray) {
|
|
|
+ // 搜索到新位置
|
|
|
mapPoint.value = data.point;
|
|
|
- // 更新地图位置
|
|
|
- if (indexMap && indexMap.kmap) {
|
|
|
- indexMap.kmap.getView().animate({
|
|
|
- center: data.coordinateArray,
|
|
|
- zoom: 16,
|
|
|
- duration: 500,
|
|
|
- });
|
|
|
- // 更新点击点
|
|
|
- if (indexMap.clickPointLayer) {
|
|
|
- indexMap.clickPointLayer.source.clear();
|
|
|
- const point = new Feature(new Point(data.coordinateArray));
|
|
|
- indexMap.clickPointLayer.addFeature(point);
|
|
|
- }
|
|
|
- }
|
|
|
- getStoreList(false);
|
|
|
+ paramsPage.value.distance = "5000";
|
|
|
+ updateMapLocation(data.coordinateArray);
|
|
|
+ resetList();
|
|
|
} else {
|
|
|
// 重置为初始位置
|
|
|
mapPoint.value = store.state.home.miniUserLocationPoint;
|
|
|
- indexMap.clickPointLayer.source.clear();
|
|
|
- // 更新区县列表
|
|
|
- getStoreList();
|
|
|
+ paramsPage.value.distance = "";
|
|
|
+
|
|
|
+ // 获取初始位置的坐标
|
|
|
+ const initialPoint = util.wktCastGeom(mapPoint.value).getFirstCoordinate();
|
|
|
+ updateMapLocation(initialPoint);
|
|
|
+
|
|
|
+ resetList();
|
|
|
+ initMap();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
const cardContentHeight = ref(245);
|
|
|
|
|
|
const distanceOptions = [
|
|
|
- { value: "1", label: "5km" },
|
|
|
- { value: "2", label: "3km" },
|
|
|
- { value: "3", label: "1km" },
|
|
|
+ { value: "不限范围", label: "不限范围" },
|
|
|
+ { value: "5000", label: "5km" },
|
|
|
+ { value: "3000", label: "3km" },
|
|
|
+ { value: "1000", label: "1km" },
|
|
|
];
|
|
|
const serviceTypeList = ref([
|
|
|
- {id: "", name: "全部"},
|
|
|
- {id: 1, name: "嫁接"},
|
|
|
- {id: 2, name: "修剪"},
|
|
|
- {id: 3, name: "施肥打药"},
|
|
|
+ {id: "全部类型", name: "全部类型"},
|
|
|
+ {id: "嫁接", name: "嫁接"},
|
|
|
+ {id: "修剪", name: "修剪"},
|
|
|
+ {id: "施肥打药", name: "施肥打药"},
|
|
|
]);
|
|
|
|
|
|
const cardContentRef = ref(null);
|
|
|
const handleHeightChange = ({ height }) => {
|
|
|
if (height === anchors.value[0]) {
|
|
|
cardContentHeight.value = 245;
|
|
|
- cardContentRef.value.scrollTo({ top: 0, behavior: "smooth" });
|
|
|
+ if (cardContentRef.value && cardContentRef.value.$el) {
|
|
|
+ cardContentRef.value.$el.scrollTo({ top: 0, behavior: "smooth" });
|
|
|
+ }
|
|
|
} else if (height === anchors.value[1]) {
|
|
|
cardContentHeight.value = Math.round(1 * window.innerHeight) - (tabBarHeight.value - 40) - 170;
|
|
|
}
|
|
|
@@ -242,7 +404,7 @@ const handleOnlineChat = ({miniUserIds}) => {
|
|
|
};
|
|
|
|
|
|
defineExpose({
|
|
|
- getStoreList,
|
|
|
+ getStoreList: resetList,
|
|
|
});
|
|
|
|
|
|
</script>
|
|
|
@@ -298,9 +460,10 @@ defineExpose({
|
|
|
.hall-content {
|
|
|
height: 100%;
|
|
|
position: relative;
|
|
|
+ .van-list-container {
|
|
|
+ overflow-y: auto;
|
|
|
+ }
|
|
|
.farm-list {
|
|
|
- overflow: auto;
|
|
|
- height: calc(100% - 60px);
|
|
|
padding: 0 12px;
|
|
|
.task-item {
|
|
|
border-top: 1px solid rgba(0, 0, 0, 0.1);
|