1020 lines
30 KiB
Vue
1020 lines
30 KiB
Vue
|
|
<template>
|
|||
|
|
<view class="search-page">
|
|||
|
|
<!-- 自定义导航栏 -->
|
|||
|
|
<custom-navbar title="搜索" :leftImg="'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/e8f7c37f-b446-41da-b578-59098a56bc4d.png'" :showUser="true"
|
|||
|
|
backgroundColor="linear-gradient( 134deg, #F52540 0%, #E8101E 100%)" titleColor="#fff"
|
|||
|
|
borderBottom="none"></custom-navbar>
|
|||
|
|
|
|||
|
|
<!-- 顶部搜索和标签区域 -->
|
|||
|
|
<view class="page-top">
|
|||
|
|
<search-box v-model="listQuery.title" placeholder="请输入手艺人/服务/店铺等" @search="search" @confirm="search" />
|
|||
|
|
<view class="tabList">
|
|||
|
|
<view @click="changeType(tab.value)" :key="tab.value" class="tabItem"
|
|||
|
|
:class="{ tabItem_active: tab.value == listQuery.type }" v-for="tab in tabList">
|
|||
|
|
{{ tab.label }}
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
<!-- 筛选组件 - 只在服务类型(3)显示 -->
|
|||
|
|
<view v-if="listQuery.type === 3" class="filter-container">
|
|||
|
|
<filterVue @filter-change="handleFilterChange"
|
|||
|
|
@open="onFilterShowChange(true)"
|
|||
|
|
@close="onFilterShowChange(false)"
|
|||
|
|
/>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 加载提示 -->
|
|||
|
|
<view v-if="isLoading" class="simple-loading">
|
|||
|
|
<view class="loading-spinner"></view>
|
|||
|
|
<text class="loading-text">搜索中...</text>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 列表容器 -->
|
|||
|
|
<view class="list-container" :class="{ 'loading-blur': isLoading }">
|
|||
|
|
<CommonList background="transparent" ref="groupRef" :apiUrl="'/user/getSearchList'"
|
|||
|
|
:listQuery="listQuery"
|
|||
|
|
:listScrollHeight="`calc(100vh - ${totalTopHeight}rpx)`"
|
|||
|
|
:key="listQuery.type + '_' + (listQuery._timestamp || '')">
|
|||
|
|
<template #listData="{ list }">
|
|||
|
|
<!-- 服务卡片 -->
|
|||
|
|
<view v-if="listQuery.type == 3 && list[0] && list[0].search_type == 3">
|
|||
|
|
<view v-for="(item, index) in list" :key="index" class="card-wrapper-service">
|
|||
|
|
<serviceCardList class="service-card" :list="[item]"></serviceCardList>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 手艺人卡片 -->
|
|||
|
|
<view v-if="listQuery.type == 1 && list[0] && list[0].search_type == 1">
|
|||
|
|
<!-- <view v-for="(item, index) in list" :key="index" class="card-wrapper-syr">
|
|||
|
|
<view class="inner-content">
|
|||
|
|
<syrCardList :list="[item]"></syrCardList>
|
|||
|
|
</view>
|
|||
|
|
</view> -->
|
|||
|
|
<syrCardList :list="list"></syrCardList>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 商家卡片 -->
|
|||
|
|
<view v-if="listQuery.type == 2 && list[0] && list[0].search_type == 2" class="shop_box">
|
|||
|
|
<view v-for="(item, index) in list" :key="index" class="card-wrapper">
|
|||
|
|
<shopCard :shop="item"></shopCard>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<!-- 空数据状态 -->
|
|||
|
|
<template #empty>
|
|||
|
|
<noData></noData>
|
|||
|
|
</template>
|
|||
|
|
</CommonList>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script>
|
|||
|
|
import CommonList from "@/components/common/CommonList.vue";
|
|||
|
|
import searchBox from "./components/search_box_result.vue";
|
|||
|
|
import syrCardList from "./components/syrCardList.vue";
|
|||
|
|
import serviceCardList from "./components/serviceCardList.vue";
|
|||
|
|
import shopCard from "./components/shopCard.vue";
|
|||
|
|
import filterVue from "./components/filter.vue";
|
|||
|
|
import http from "@/utils/request.js";
|
|||
|
|
|
|||
|
|
export default {
|
|||
|
|
components: {
|
|||
|
|
searchBox,
|
|||
|
|
CommonList,
|
|||
|
|
syrCardList,
|
|||
|
|
serviceCardList,
|
|||
|
|
shopCard,
|
|||
|
|
filterVue,
|
|||
|
|
},
|
|||
|
|
data() {
|
|||
|
|
return {
|
|||
|
|
totalTopHeight: 300, // 顶部区域总高度估算值,会在onLoad中重新计算
|
|||
|
|
listQuery: {
|
|||
|
|
page: 1,
|
|||
|
|
limit: 10,
|
|||
|
|
type: 3,
|
|||
|
|
title: "",
|
|||
|
|
userLngz: "",
|
|||
|
|
userLat: "",
|
|||
|
|
// 筛选参数
|
|||
|
|
serviceType: null,
|
|||
|
|
distance: null,
|
|||
|
|
minPrice: null,
|
|||
|
|
maxPrice: null,
|
|||
|
|
sortType: null,
|
|||
|
|
// 用于强制刷新的时间戳
|
|||
|
|
_timestamp: 0,
|
|||
|
|
},
|
|||
|
|
tabList: [
|
|||
|
|
{
|
|||
|
|
label: "服务",
|
|||
|
|
value: 3
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
label: "手艺人",
|
|||
|
|
value: 1
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
{
|
|||
|
|
label: "门店",
|
|||
|
|
value: 2
|
|||
|
|
},
|
|||
|
|
],
|
|||
|
|
isSearching: false, // 防止重复搜索
|
|||
|
|
searchTimer: null, // 防抖定时器
|
|||
|
|
hasInitialSearch: false, // 标记是否已执行初始智能搜索
|
|||
|
|
userChangedTab: false, // 标记用户是否手动切换过tab
|
|||
|
|
lastSearchText: "", // 记录上一次的搜索词
|
|||
|
|
forceNormalSearch: false, // 强制普通搜索标志
|
|||
|
|
isLoading: false, // 加载状态
|
|||
|
|
loadingTimer: null, // 加载计时器(防止加载时间过短闪烁)
|
|||
|
|
minLoadingTime: 100, // 最小加载显示时间(毫秒)
|
|||
|
|
};
|
|||
|
|
},
|
|||
|
|
onLoad(options) {
|
|||
|
|
// 接收可能的搜索词参数
|
|||
|
|
this.listQuery.title = options.searchText || '';
|
|||
|
|
this.lastSearchText = this.listQuery.title; // 记录初始搜索词
|
|||
|
|
|
|||
|
|
// 获取用户地理位置
|
|||
|
|
let userAdrees = uni.getStorageSync("userAdrees");
|
|||
|
|
if (userAdrees) {
|
|||
|
|
this.listQuery.userLngz = userAdrees.longitude;
|
|||
|
|
this.listQuery.userLat = userAdrees.latitude;
|
|||
|
|
} else {
|
|||
|
|
// 默认位置(备用)
|
|||
|
|
this.listQuery.userLngz = "117.330043";
|
|||
|
|
this.listQuery.userLat = "38.372266";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
this.$nextTick(() => {
|
|||
|
|
this.showLoading();
|
|||
|
|
// 只在第一次进入页面时执行智能搜索
|
|||
|
|
this.performInitialSearch();
|
|||
|
|
|
|||
|
|
// 延迟计算高度,确保DOM渲染完成
|
|||
|
|
setTimeout(() => {
|
|||
|
|
this.calculateTopHeight();
|
|||
|
|
}, 100);
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
methods: {
|
|||
|
|
// 计算顶部区域总高度(用于设置列表滚动区域高度)
|
|||
|
|
calculateTopHeight() {
|
|||
|
|
// 创建选择器查询
|
|||
|
|
const query = uni.createSelectorQuery().in(this);
|
|||
|
|
|
|||
|
|
// 查询导航栏高度
|
|||
|
|
query.select('.custom-navbar').boundingClientRect();
|
|||
|
|
query.select('.page-top').boundingClientRect();
|
|||
|
|
|
|||
|
|
query.exec((res) => {
|
|||
|
|
if (res[0] && res[1]) {
|
|||
|
|
const navHeight = res[0].height;
|
|||
|
|
const topHeight = res[1].height;
|
|||
|
|
const systemInfo = uni.getSystemInfoSync();
|
|||
|
|
const pxToRpx = 750 / systemInfo.windowWidth;
|
|||
|
|
|
|||
|
|
// 转换为rpx并四舍五入
|
|||
|
|
const totalHeight = Math.ceil((navHeight + topHeight) * pxToRpx);
|
|||
|
|
this.totalTopHeight = totalHeight;
|
|||
|
|
|
|||
|
|
console.log('导航栏高度(px):', navHeight);
|
|||
|
|
console.log('顶部区域高度(px):', topHeight);
|
|||
|
|
console.log('总高度(rpx):', totalHeight);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
// 返回上一页
|
|||
|
|
goBack() {
|
|||
|
|
// #ifdef APP-PLUS
|
|||
|
|
uni.navigateBack();
|
|||
|
|
// #endif
|
|||
|
|
|
|||
|
|
// #ifdef MP-WEIXIN
|
|||
|
|
uni.navigateBack({
|
|||
|
|
delta: 1,
|
|||
|
|
});
|
|||
|
|
// #endif
|
|||
|
|
},
|
|||
|
|
// 切换tab类型
|
|||
|
|
changeType(type) {
|
|||
|
|
// 标记用户手动切换tab
|
|||
|
|
this.userChangedTab = true;
|
|||
|
|
// 切换到指定tab后,后续搜索都使用普通搜索(不检查其他tab)
|
|||
|
|
this.forceNormalSearch = true;
|
|||
|
|
|
|||
|
|
this.listQuery.type = type;
|
|||
|
|
// 切换类型时重置筛选参数(只有服务类型才有筛选)
|
|||
|
|
if (type !== 3) {
|
|||
|
|
this.resetFilterParams();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 显示加载
|
|||
|
|
this.showLoading();
|
|||
|
|
// 用户手动切换tab时,只执行普通搜索
|
|||
|
|
this.performNormalSearch();
|
|||
|
|
|
|||
|
|
// 重新计算高度(因为筛选组件可能显示/隐藏)
|
|||
|
|
this.$nextTick(() => {
|
|||
|
|
setTimeout(() => {
|
|||
|
|
this.calculateTopHeight();
|
|||
|
|
}, 50);
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
// 搜索方法(点击搜索按钮或确认搜索时触发)
|
|||
|
|
search() {
|
|||
|
|
// 防抖处理,避免频繁请求
|
|||
|
|
if (this.searchTimer) {
|
|||
|
|
clearTimeout(this.searchTimer);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 如果正在搜索中,等待
|
|||
|
|
if (this.isSearching) {
|
|||
|
|
console.log('正在搜索中,稍后重试');
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
this.searchTimer = setTimeout(() => {
|
|||
|
|
// 检查搜索词是否有变化
|
|||
|
|
const searchTextChanged = this.listQuery.title !== this.lastSearchText;
|
|||
|
|
const isEmptySearch = !this.listQuery.title || this.listQuery.title.trim() === '';
|
|||
|
|
|
|||
|
|
// 如果搜索框为空,直接获取当前类型的全部数据
|
|||
|
|
if (isEmptySearch) {
|
|||
|
|
console.log('搜索框为空,直接获取全部数据');
|
|||
|
|
this.lastSearchText = '';
|
|||
|
|
this.showLoading();
|
|||
|
|
// 清空搜索词,确保后端能正确返回全部数据
|
|||
|
|
this.listQuery.title = '';
|
|||
|
|
// 重置页码,从第一页开始
|
|||
|
|
this.listQuery.page = 1;
|
|||
|
|
// 添加时间戳强制刷新列表
|
|||
|
|
this.listQuery._timestamp = Date.now();
|
|||
|
|
// 执行搜索
|
|||
|
|
this.performNormalSearch();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 如果搜索词有变化,重置智能搜索状态
|
|||
|
|
if (searchTextChanged) {
|
|||
|
|
console.log('搜索词变化,重置智能搜索状态');
|
|||
|
|
this.lastSearchText = this.listQuery.title;
|
|||
|
|
this.hasInitialSearch = false;
|
|||
|
|
this.forceNormalSearch = false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 显示加载动画
|
|||
|
|
this.showLoading();
|
|||
|
|
|
|||
|
|
// 根据当前状态决定使用哪种搜索方式
|
|||
|
|
if (this.forceNormalSearch) {
|
|||
|
|
console.log('强制使用普通搜索');
|
|||
|
|
this.performNormalSearch();
|
|||
|
|
} else if (this.userChangedTab) {
|
|||
|
|
// 用户已经切换过tab,但搜索词变化了,可以考虑重新智能搜索
|
|||
|
|
if (searchTextChanged && !this.hasInitialSearch) {
|
|||
|
|
console.log('用户切换过tab但搜索词变化,尝试智能搜索');
|
|||
|
|
this.performSmartSearch();
|
|||
|
|
} else {
|
|||
|
|
console.log('用户切换过tab且搜索词未变,使用普通搜索');
|
|||
|
|
this.performNormalSearch();
|
|||
|
|
}
|
|||
|
|
} else if (!this.hasInitialSearch) {
|
|||
|
|
console.log('首次或搜索词变化,使用智能搜索');
|
|||
|
|
this.performSmartSearch();
|
|||
|
|
} else {
|
|||
|
|
console.log('已经执行过智能搜索,使用普通搜索');
|
|||
|
|
// 否则执行普通搜索
|
|||
|
|
this.performNormalSearch();
|
|||
|
|
}
|
|||
|
|
}, 300);
|
|||
|
|
},
|
|||
|
|
// 显示加载动画
|
|||
|
|
showLoading() {
|
|||
|
|
if (this.loadingTimer) {
|
|||
|
|
clearTimeout(this.loadingTimer);
|
|||
|
|
}
|
|||
|
|
this.isLoading = true;
|
|||
|
|
},
|
|||
|
|
// 隐藏加载动画(带最小时间,避免闪烁)
|
|||
|
|
hideLoading() {
|
|||
|
|
if (this.loadingTimer) {
|
|||
|
|
clearTimeout(this.loadingTimer);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 确保至少显示一定时间,避免闪烁
|
|||
|
|
this.loadingTimer = setTimeout(() => {
|
|||
|
|
this.isLoading = false;
|
|||
|
|
this.loadingTimer = null;
|
|||
|
|
console.log('加载动画已隐藏');
|
|||
|
|
}, this.minLoadingTime);
|
|||
|
|
},
|
|||
|
|
// 立即隐藏加载动画(用于错误情况)
|
|||
|
|
forceHideLoading() {
|
|||
|
|
if (this.loadingTimer) {
|
|||
|
|
clearTimeout(this.loadingTimer);
|
|||
|
|
this.loadingTimer = null;
|
|||
|
|
}
|
|||
|
|
this.isLoading = false;
|
|||
|
|
console.log('强制隐藏加载动画');
|
|||
|
|
},
|
|||
|
|
// 首次进入页面时的智能搜索 - 哪个列表先有数据就显示哪个
|
|||
|
|
async performInitialSearch() {
|
|||
|
|
if (this.isSearching) return;
|
|||
|
|
|
|||
|
|
this.isSearching = true;
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
// 如果没有搜索关键词,直接获取当前类型的全部数据
|
|||
|
|
if (!this.listQuery.title || this.listQuery.title.trim() === '') {
|
|||
|
|
console.log('搜索框为空,获取全部数据');
|
|||
|
|
// 清空title参数,让后端返回全部数据
|
|||
|
|
this.listQuery.title = '';
|
|||
|
|
// 重置页码
|
|||
|
|
this.listQuery.page = 1;
|
|||
|
|
// 添加时间戳强制刷新
|
|||
|
|
this.listQuery._timestamp = Date.now();
|
|||
|
|
// 刷新当前列表
|
|||
|
|
this.refreshCurrentList();
|
|||
|
|
this.hasInitialSearch = true;
|
|||
|
|
// 延迟隐藏加载
|
|||
|
|
setTimeout(() => this.hideLoading(), 100);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 记录原始类型
|
|||
|
|
const originalType = this.listQuery.type;
|
|||
|
|
console.log('初始智能搜索开始,当前类型:', originalType, '搜索词:', this.listQuery.title);
|
|||
|
|
|
|||
|
|
// 检查三个类型哪个先有数据
|
|||
|
|
const targetType = await this.findFirstAvailableType();
|
|||
|
|
console.log('初始智能搜索结束,找到的目标类型:', targetType, '当前类型:', this.listQuery.type);
|
|||
|
|
|
|||
|
|
// 如果找到有数据的类型,切换到该类型
|
|||
|
|
if (targetType && targetType !== this.listQuery.type) {
|
|||
|
|
console.log(`初始切换到类型 ${targetType}`);
|
|||
|
|
this.listQuery.type = targetType;
|
|||
|
|
// 如果是服务类型,保留筛选参数,否则重置
|
|||
|
|
if (targetType !== 3) {
|
|||
|
|
this.resetFilterParams();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 重置页码
|
|||
|
|
this.listQuery.page = 1;
|
|||
|
|
// 添加时间戳
|
|||
|
|
this.listQuery._timestamp = Date.now();
|
|||
|
|
|
|||
|
|
// 更新UI后刷新列表
|
|||
|
|
this.$nextTick(() => {
|
|||
|
|
this.refreshCurrentList();
|
|||
|
|
});
|
|||
|
|
} else if (targetType === this.listQuery.type) {
|
|||
|
|
// 类型没变,直接刷新
|
|||
|
|
console.log(`初始类型未变 ${targetType},直接刷新`);
|
|||
|
|
// 重置页码
|
|||
|
|
this.listQuery.page = 1;
|
|||
|
|
// 添加时间戳
|
|||
|
|
this.listQuery._timestamp = Date.now();
|
|||
|
|
this.refreshCurrentList();
|
|||
|
|
} else {
|
|||
|
|
// 没找到有数据的类型,显示空状态
|
|||
|
|
console.log(`初始未找到有数据的类型,保持当前类型 ${this.listQuery.type}`);
|
|||
|
|
// 重置页码
|
|||
|
|
this.listQuery.page = 1;
|
|||
|
|
// 添加时间戳
|
|||
|
|
this.listQuery._timestamp = Date.now();
|
|||
|
|
this.refreshCurrentList();
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('初始智能搜索失败:', error);
|
|||
|
|
// 出错时直接刷新当前列表
|
|||
|
|
this.refreshCurrentList();
|
|||
|
|
} finally {
|
|||
|
|
this.isSearching = false;
|
|||
|
|
this.hasInitialSearch = true; // 标记初始搜索已完成
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
// 普通搜索(用于用户手动操作后)
|
|||
|
|
performNormalSearch() {
|
|||
|
|
if (this.isSearching) return;
|
|||
|
|
|
|||
|
|
this.isSearching = true;
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
console.log('普通搜索,类型:', this.listQuery.type, '搜索词:', this.listQuery.title);
|
|||
|
|
// 确保空搜索时也能正确请求
|
|||
|
|
if (!this.listQuery.title || this.listQuery.title.trim() === '') {
|
|||
|
|
this.listQuery.title = '';
|
|||
|
|
}
|
|||
|
|
// 重置页码
|
|||
|
|
this.listQuery.page = 1;
|
|||
|
|
// 添加时间戳强制刷新
|
|||
|
|
this.listQuery._timestamp = Date.now();
|
|||
|
|
// 刷新列表
|
|||
|
|
this.refreshCurrentList();
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('普通搜索失败:', error);
|
|||
|
|
this.forceHideLoading();
|
|||
|
|
} finally {
|
|||
|
|
this.isSearching = false;
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
// 智能搜索(只在第一次搜索时使用)
|
|||
|
|
async performSmartSearch() {
|
|||
|
|
if (this.isSearching) return;
|
|||
|
|
|
|||
|
|
this.isSearching = true;
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
// 如果没有搜索关键词,直接刷新当前列表(获取全部数据)
|
|||
|
|
if (!this.listQuery.title || this.listQuery.title.trim() === '') {
|
|||
|
|
this.listQuery.title = '';
|
|||
|
|
// 重置页码
|
|||
|
|
this.listQuery.page = 1;
|
|||
|
|
// 添加时间戳
|
|||
|
|
this.listQuery._timestamp = Date.now();
|
|||
|
|
this.refreshCurrentList();
|
|||
|
|
this.hasInitialSearch = true;
|
|||
|
|
setTimeout(() => this.hideLoading(), 100);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 记录原始类型
|
|||
|
|
const originalType = this.listQuery.type;
|
|||
|
|
console.log('智能搜索开始,当前类型:', originalType, '搜索词:', this.listQuery.title);
|
|||
|
|
|
|||
|
|
// 先检查当前tab是否有数据
|
|||
|
|
const currentTypeHasData = await this.checkTypeHasData(this.listQuery.type);
|
|||
|
|
|
|||
|
|
if (currentTypeHasData) {
|
|||
|
|
// 当前tab有数据,直接使用当前tab
|
|||
|
|
console.log(`当前类型 ${this.listQuery.type} 有数据,直接刷新`);
|
|||
|
|
// 重置页码
|
|||
|
|
this.listQuery.page = 1;
|
|||
|
|
// 添加时间戳
|
|||
|
|
this.listQuery._timestamp = Date.now();
|
|||
|
|
this.refreshCurrentList();
|
|||
|
|
} else {
|
|||
|
|
// 当前tab没有数据,查找其他有数据的tab
|
|||
|
|
const targetType = await this.findFirstAvailableType();
|
|||
|
|
console.log('智能搜索结束,找到的目标类型:', targetType, '当前类型:', this.listQuery.type);
|
|||
|
|
|
|||
|
|
if (targetType && targetType !== this.listQuery.type) {
|
|||
|
|
console.log(`切换到类型 ${targetType}`);
|
|||
|
|
this.listQuery.type = targetType;
|
|||
|
|
// 如果是服务类型,保留筛选参数,否则重置
|
|||
|
|
if (targetType !== 3) {
|
|||
|
|
this.resetFilterParams();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 重置页码
|
|||
|
|
this.listQuery.page = 1;
|
|||
|
|
// 添加时间戳
|
|||
|
|
this.listQuery._timestamp = Date.now();
|
|||
|
|
|
|||
|
|
// 更新UI后刷新列表
|
|||
|
|
this.$nextTick(() => {
|
|||
|
|
this.refreshCurrentList();
|
|||
|
|
});
|
|||
|
|
} else if (targetType === this.listQuery.type) {
|
|||
|
|
console.log(`类型未变 ${targetType},直接刷新`);
|
|||
|
|
// 重置页码
|
|||
|
|
this.listQuery.page = 1;
|
|||
|
|
// 添加时间戳
|
|||
|
|
this.listQuery._timestamp = Date.now();
|
|||
|
|
this.refreshCurrentList();
|
|||
|
|
} else {
|
|||
|
|
// 没找到有数据的类型,显示空状态
|
|||
|
|
console.log(`未找到有数据的类型,保持当前类型 ${this.listQuery.type}`);
|
|||
|
|
// 重置页码
|
|||
|
|
this.listQuery.page = 1;
|
|||
|
|
// 添加时间戳
|
|||
|
|
this.listQuery._timestamp = Date.now();
|
|||
|
|
this.refreshCurrentList();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('智能搜索失败:', error);
|
|||
|
|
this.refreshCurrentList();
|
|||
|
|
} finally {
|
|||
|
|
this.isSearching = false;
|
|||
|
|
this.hasInitialSearch = true;
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
// 查找第一个有数据的类型
|
|||
|
|
async findFirstAvailableType() {
|
|||
|
|
const typesToCheck = [3, 1, 2]; // 服务 -> 手艺人 -> 店铺
|
|||
|
|
|
|||
|
|
// 智能判断优先检查哪个类型
|
|||
|
|
let priorityTypes = typesToCheck;
|
|||
|
|
|
|||
|
|
// 如果当前搜索词是之前搜索过的,并且用户已经选择了某个tab
|
|||
|
|
// 则优先检查用户选择的tab
|
|||
|
|
if (this.userChangedTab && this.listQuery.type) {
|
|||
|
|
console.log(`用户已选择类型 ${this.listQuery.type},优先检查`);
|
|||
|
|
priorityTypes = [
|
|||
|
|
this.listQuery.type,
|
|||
|
|
...typesToCheck.filter(type => type !== this.listQuery.type)
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for (const type of priorityTypes) {
|
|||
|
|
try {
|
|||
|
|
console.log(`开始检查类型 ${type} 是否有数据`);
|
|||
|
|
const hasData = await this.checkTypeHasData(type);
|
|||
|
|
console.log(`类型 ${type} 检查结果: ${hasData ? '有数据' : '无数据'}`);
|
|||
|
|
|
|||
|
|
if (hasData) {
|
|||
|
|
console.log(`类型 ${type} 有数据,将切换到该类型`);
|
|||
|
|
return type;
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error(`检查类型 ${type} 失败:`, error);
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 都没找到数据,返回null
|
|||
|
|
console.log('所有类型都没有数据');
|
|||
|
|
return null;
|
|||
|
|
},
|
|||
|
|
// 检查指定类型是否有数据
|
|||
|
|
async checkTypeHasData(type) {
|
|||
|
|
try {
|
|||
|
|
// 构建检查参数 注意这里title要传空字符串而不是不传
|
|||
|
|
const params = {
|
|||
|
|
page: 1,
|
|||
|
|
limit: 5, // 只检查前5条,减少请求数据量
|
|||
|
|
type: type,
|
|||
|
|
// 这里必须传title,即使是空字符串也要传
|
|||
|
|
title: this.listQuery.title || '',
|
|||
|
|
userLngz: this.listQuery.userLngz,
|
|||
|
|
userLat: this.listQuery.userLat,
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 如果是服务类型,添加筛选参数
|
|||
|
|
if (type === 3) {
|
|||
|
|
if (this.listQuery.serviceType !== null && this.listQuery.serviceType !== undefined) {
|
|||
|
|
params.server_kind = this.listQuery.serviceType;
|
|||
|
|
}
|
|||
|
|
if (this.listQuery.distance !== null && this.listQuery.distance !== undefined) {
|
|||
|
|
params.distance = this.listQuery.distance / 1000;
|
|||
|
|
}
|
|||
|
|
if (this.listQuery.minPrice !== null && this.listQuery.minPrice !== undefined) {
|
|||
|
|
params.min_price = this.listQuery.minPrice;
|
|||
|
|
}
|
|||
|
|
if (this.listQuery.maxPrice !== null && this.listQuery.maxPrice !== undefined) {
|
|||
|
|
params.max_price = this.listQuery.maxPrice;
|
|||
|
|
}
|
|||
|
|
if (this.listQuery.sortType !== null && this.listQuery.sortType !== undefined) {
|
|||
|
|
const sortMap = {
|
|||
|
|
'distance': 1,
|
|||
|
|
'sales': 2,
|
|||
|
|
'price_asc': 3,
|
|||
|
|
'price_desc': 4,
|
|||
|
|
};
|
|||
|
|
params.sort_type = sortMap[this.listQuery.sortType] || 1;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 清理空值参数,但保留title为空字符串的情况
|
|||
|
|
Object.keys(params).forEach(key => {
|
|||
|
|
if (params[key] === null || params[key] === undefined) {
|
|||
|
|
delete params[key];
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
console.log(`检查类型 ${type} 的请求参数:`, JSON.stringify(params));
|
|||
|
|
|
|||
|
|
// 使用项目的请求封装
|
|||
|
|
const res = await http.post('/user/getSearchList', params, {
|
|||
|
|
hideLoading: true
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
let hasData = false;
|
|||
|
|
|
|||
|
|
// 尝试多种可能的数据结构
|
|||
|
|
// 可能性1: res 直接包含 list 数组
|
|||
|
|
if (res && res.list && Array.isArray(res.list) && res.list.length > 0) {
|
|||
|
|
console.log(`√类型 ${type}: 通过 res.list 找到数据, 长度: ${res.list.length}`);
|
|||
|
|
hasData = true;
|
|||
|
|
}
|
|||
|
|
// 可能性2: res.data 中包含 list 数组
|
|||
|
|
else if (res && res.data && res.data.list && Array.isArray(res.data.list) && res.data.list.length > 0) {
|
|||
|
|
console.log(`√ 类型 ${type}: 通过 res.data.list 找到数据, 长度: ${res.data.list.length}`);
|
|||
|
|
hasData = true;
|
|||
|
|
}
|
|||
|
|
// 可能性3: res 本身是数组
|
|||
|
|
else if (res && Array.isArray(res) && res.length > 0) {
|
|||
|
|
console.log(`√ 类型 ${type}: res 本身就是数组, 长度: ${res.length}`);
|
|||
|
|
hasData = true;
|
|||
|
|
}
|
|||
|
|
// 可能性4: 根据 CommonList 的日志,可能是其他数据结构
|
|||
|
|
else if (res && res.data && Array.isArray(res.data) && res.data.length > 0) {
|
|||
|
|
console.log(`√类型 ${type}: 通过 res.data 数组找到数据, 长度: ${res.data.length}`);
|
|||
|
|
hasData = true;
|
|||
|
|
}
|
|||
|
|
else {
|
|||
|
|
console.log(`× 类型 ${type}: 未找到数据, 响应结构不符合预期`);
|
|||
|
|
hasData = false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return hasData;
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('检查数据失败:', error);
|
|||
|
|
// 请求失败,认为没有数据
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
// 判断是否为可能的手艺人名称
|
|||
|
|
isPossibleArtisanName(text) {
|
|||
|
|
if (!text) return false;
|
|||
|
|
|
|||
|
|
// 手艺人名称通常是中文,且较短
|
|||
|
|
const chineseRegex = /^[\u4e00-\u9fa5]{2,4}$/;
|
|||
|
|
return chineseRegex.test(text);
|
|||
|
|
},
|
|||
|
|
// 刷新当前列表
|
|||
|
|
refreshCurrentList() {
|
|||
|
|
// 构建API请求参数
|
|||
|
|
const apiParams = this.buildApiParams();
|
|||
|
|
console.log('刷新列表,类型:', this.listQuery.type, '搜索词:', this.listQuery.title, 'API请求参数:', apiParams);
|
|||
|
|
|
|||
|
|
// 强制更新listQuery
|
|||
|
|
this.listQuery = {
|
|||
|
|
...this.listQuery,
|
|||
|
|
...apiParams,
|
|||
|
|
// 确保时间戳更新
|
|||
|
|
_timestamp: Date.now()
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
console.log('更新后的listQuery:', JSON.parse(JSON.stringify(this.listQuery)));
|
|||
|
|
|
|||
|
|
// 检查CommonList组件是否存在
|
|||
|
|
if (!this.$refs.groupRef) {
|
|||
|
|
console.error('CommonList组件引用不存在');
|
|||
|
|
this.forceHideLoading();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
console.log('开始调用manualRefresh...');
|
|||
|
|
|
|||
|
|
// 等待一个tick确保DOM更新
|
|||
|
|
this.$nextTick(() => {
|
|||
|
|
try {
|
|||
|
|
// 尝试调用CommonList的刷新方法
|
|||
|
|
if (this.$refs.groupRef.manualRefresh) {
|
|||
|
|
console.log('调用manualRefresh方法,参数:', apiParams);
|
|||
|
|
this.$refs.groupRef.manualRefresh(apiParams);
|
|||
|
|
} else {
|
|||
|
|
console.warn('CommonList没有找到manualRefresh方法');
|
|||
|
|
// 如果manualRefresh不存在,尝试其他方式
|
|||
|
|
this.$forceUpdate();
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('调用manualRefresh失败:', error);
|
|||
|
|
this.forceHideLoading();
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 监听列表刷新完成(使用一个简单的超时机制)
|
|||
|
|
// 因为CommonList可能没有提供完成事件,我们设置一个合理的超时
|
|||
|
|
setTimeout(() => {
|
|||
|
|
this.hideLoading();
|
|||
|
|
console.log('列表刷新超时检查,隐藏加载动画');
|
|||
|
|
}, 300);
|
|||
|
|
},
|
|||
|
|
// 构建符合后端接口的参数
|
|||
|
|
buildApiParams() {
|
|||
|
|
const params = {
|
|||
|
|
page: this.listQuery.page,
|
|||
|
|
limit: this.listQuery.limit,
|
|||
|
|
type: this.listQuery.type,
|
|||
|
|
// 关键:这里title必须传,即使为空字符串也要传
|
|||
|
|
// 因为后端可能依赖title参数来判断是搜索还是获取全部
|
|||
|
|
// 如果后端不需要title参数来表示全部,那么传空字符串应该也是可以的
|
|||
|
|
title: this.listQuery.title || '',
|
|||
|
|
userLngz: this.listQuery.userLngz,
|
|||
|
|
userLat: this.listQuery.userLat,
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 只有服务类型(type=3)时才添加筛选参数
|
|||
|
|
if (this.listQuery.type === 3) {
|
|||
|
|
// 服务类型映射: serviceType -> server_kind
|
|||
|
|
if (this.listQuery.serviceType !== null && this.listQuery.serviceType !== undefined) {
|
|||
|
|
params.server_kind = this.listQuery.serviceType;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 距离筛选: 前端是米,后端是公里
|
|||
|
|
if (this.listQuery.distance !== null && this.listQuery.distance !== undefined) {
|
|||
|
|
params.distance = this.listQuery.distance / 1000; // 米转公里
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 价格筛选
|
|||
|
|
if (this.listQuery.minPrice !== null && this.listQuery.minPrice !== undefined) {
|
|||
|
|
params.min_price = this.listQuery.minPrice;
|
|||
|
|
}
|
|||
|
|
if (this.listQuery.maxPrice !== null && this.listQuery.maxPrice !== undefined) {
|
|||
|
|
params.max_price = this.listQuery.maxPrice;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 排序类型映射
|
|||
|
|
if (this.listQuery.sortType !== null && this.listQuery.sortType !== undefined) {
|
|||
|
|
// 将前端的排序值映射为后端的数字
|
|||
|
|
const sortMap = {
|
|||
|
|
'distance': 1, // 离我最近
|
|||
|
|
'sales': 2, // 销量最高
|
|||
|
|
'price_asc': 3, // 价格最低
|
|||
|
|
'price_desc': 4, // 价格最高
|
|||
|
|
};
|
|||
|
|
params.sort_type = sortMap[this.listQuery.sortType] || 1;
|
|||
|
|
}
|
|||
|
|
}else{
|
|||
|
|
params.server_kind =null
|
|||
|
|
params.distance =null
|
|||
|
|
params.min_price =null
|
|||
|
|
params.max_price =null
|
|||
|
|
params.sortMap =null
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 清理空值参数,但保留title为空字符串的情况
|
|||
|
|
Object.keys(params).forEach(key => {
|
|||
|
|
if (params[key] === null || params[key] === undefined) {
|
|||
|
|
delete params[key];
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
console.log('最终构建的参数:', JSON.stringify(params));
|
|||
|
|
return params;
|
|||
|
|
},
|
|||
|
|
// 重置筛选参数
|
|||
|
|
resetFilterParams() {
|
|||
|
|
this.$set(this.listQuery,"serviceType",null)
|
|||
|
|
this.$set(this.listQuery,"distance",null)
|
|||
|
|
this.$set(this.listQuery,"minPrice",null)
|
|||
|
|
this.$set(this.listQuery,"maxPrice",null)
|
|||
|
|
this.$set(this.listQuery,"sortType",null)
|
|||
|
|
},
|
|||
|
|
// 处理筛选变化
|
|||
|
|
handleFilterChange(filterData) {
|
|||
|
|
console.log('收到筛选数据:', filterData);
|
|||
|
|
// 更新筛选参数
|
|||
|
|
this.listQuery.serviceType = filterData.serviceType;
|
|||
|
|
this.listQuery.distance = filterData.distance;
|
|||
|
|
this.listQuery.minPrice = filterData.minPrice;
|
|||
|
|
this.listQuery.maxPrice = filterData.maxPrice;
|
|||
|
|
this.listQuery.sortType = filterData.sortType;
|
|||
|
|
|
|||
|
|
// 重置页码
|
|||
|
|
this.listQuery.page = 1;
|
|||
|
|
// 添加时间戳
|
|||
|
|
this.listQuery._timestamp = Date.now();
|
|||
|
|
|
|||
|
|
// 显示加载
|
|||
|
|
this.showLoading();
|
|||
|
|
// 重新搜索
|
|||
|
|
this.performNormalSearch();
|
|||
|
|
},
|
|||
|
|
// 筛选面板状态变化
|
|||
|
|
onFilterShowChange(show) {
|
|||
|
|
// 筛选面板显示/隐藏时重新计算高度
|
|||
|
|
this.$nextTick(() => {
|
|||
|
|
setTimeout(() => {
|
|||
|
|
this.calculateTopHeight();
|
|||
|
|
}, 300);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
onUnload() {
|
|||
|
|
// 页面卸载时清理计时器
|
|||
|
|
if (this.searchTimer) {
|
|||
|
|
clearTimeout(this.searchTimer);
|
|||
|
|
this.searchTimer = null;
|
|||
|
|
}
|
|||
|
|
if (this.loadingTimer) {
|
|||
|
|
clearTimeout(this.loadingTimer);
|
|||
|
|
this.loadingTimer = null;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<style lang="less">
|
|||
|
|
.search-page {
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
height: 100vh;
|
|||
|
|
background: #ffffff;
|
|||
|
|
position: relative;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 自定义导航栏 */
|
|||
|
|
.custom-navbar {
|
|||
|
|
flex-shrink: 0;
|
|||
|
|
position: relative;
|
|||
|
|
z-index: 1000;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 顶部区域 */
|
|||
|
|
.page-top {
|
|||
|
|
// position: fixed;
|
|||
|
|
flex-shrink: 0;
|
|||
|
|
top: calc(88rpx + var(--status-bar-height));
|
|||
|
|
left: 0;
|
|||
|
|
right: 0;
|
|||
|
|
z-index: 999;
|
|||
|
|
background: #fff;
|
|||
|
|
padding-bottom: 0;
|
|||
|
|
width: 100%;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
/* 筛选容器 */
|
|||
|
|
.filter-container {
|
|||
|
|
background: #fff;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.tabList {
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: center;
|
|||
|
|
align-items: center;
|
|||
|
|
gap: 128rpx;
|
|||
|
|
color: #333333;
|
|||
|
|
padding-bottom: 24rpx;
|
|||
|
|
background: #fff;
|
|||
|
|
|
|||
|
|
.tabItem {
|
|||
|
|
padding: 30rpx 25rpx 12rpx 25rpx;
|
|||
|
|
font-weight: 500;
|
|||
|
|
font-size: 30rpx;
|
|||
|
|
color: #333333;
|
|||
|
|
line-height: 42rpx;
|
|||
|
|
text-align: left;
|
|||
|
|
font-style: normal;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.tabItem_active {
|
|||
|
|
font-weight: 500;
|
|||
|
|
font-size: 30rpx;
|
|||
|
|
color: #E8101E;
|
|||
|
|
line-height: 42rpx;
|
|||
|
|
text-align: left;
|
|||
|
|
font-style: normal;
|
|||
|
|
position: relative;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.tabItem_active::after {
|
|||
|
|
content: "";
|
|||
|
|
position: absolute;
|
|||
|
|
bottom: 0rpx;
|
|||
|
|
left: 50%;
|
|||
|
|
-webkit-transform: translateX(-50%);
|
|||
|
|
transform: translateX(-50%);
|
|||
|
|
width: 34rpx;
|
|||
|
|
height: 6rpx;
|
|||
|
|
background: linear-gradient(306deg, #e8101e 0%, #fc563b 100%);
|
|||
|
|
border-radius: 2rpx;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 简单加载提示 */
|
|||
|
|
.simple-loading {
|
|||
|
|
position: fixed;
|
|||
|
|
top: 50%;
|
|||
|
|
left: 50%;
|
|||
|
|
transform: translate(-50%, -50%);
|
|||
|
|
z-index: 1001;
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
background: rgba(255, 255, 255, 0.95);
|
|||
|
|
padding: 40rpx 60rpx;
|
|||
|
|
border-radius: 20rpx;
|
|||
|
|
box-shadow: 0 8rpx 30rpx rgba(0, 0, 0, 0.12);
|
|||
|
|
min-width: 200rpx;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.loading-spinner {
|
|||
|
|
width: 60rpx;
|
|||
|
|
height: 60rpx;
|
|||
|
|
border: 4rpx solid #f3f3f3;
|
|||
|
|
border-top: 4rpx solid #E8101E;
|
|||
|
|
border-radius: 50%;
|
|||
|
|
animation: spin 1s linear infinite;
|
|||
|
|
margin-bottom: 24rpx;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@keyframes spin {
|
|||
|
|
0% { transform: rotate(0deg); }
|
|||
|
|
100% { transform: rotate(360deg); }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.loading-text {
|
|||
|
|
font-size: 28rpx;
|
|||
|
|
color: #666;
|
|||
|
|
font-weight: 500;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 列表容器 */
|
|||
|
|
.list-container {
|
|||
|
|
flex: 1;
|
|||
|
|
position: relative;
|
|||
|
|
background: #f5f5f5;
|
|||
|
|
overflow: hidden;
|
|||
|
|
margin-top: 0;
|
|||
|
|
padding: 0 !important;
|
|||
|
|
transition: filter 0.3s ease;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.list-container.loading-blur {
|
|||
|
|
filter: blur(2px);
|
|||
|
|
pointer-events: none;
|
|||
|
|
user-select: none;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.list-container ::v-deep .common-list {
|
|||
|
|
height: 100% !important;
|
|||
|
|
background-color: #ffffff !important;
|
|||
|
|
padding: 0 !important;
|
|||
|
|
margin: -13rpx 0 !important;
|
|||
|
|
min-height: unset !important;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.list-container ::v-deep .list-scroll {
|
|||
|
|
height: 100% !important;
|
|||
|
|
|
|||
|
|
.scroll-view {
|
|||
|
|
height: 100% !important;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.list-container {
|
|||
|
|
padding: 1rpx 0 0 0; // ???为什么
|
|||
|
|
min-height: 100%;
|
|||
|
|
background: #f5f5f5;
|
|||
|
|
border-radius: 30rpx 30rpx 0 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.list-item {
|
|||
|
|
display: block;
|
|||
|
|
padding: 0;
|
|||
|
|
background-color: transparent;
|
|||
|
|
margin-bottom: 20rpx;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 卡片样式 */
|
|||
|
|
.card-wrapper {
|
|||
|
|
background: #ffffff;
|
|||
|
|
border-radius: 20rpx;
|
|||
|
|
padding: 24rpx;
|
|||
|
|
margin: 20rpx 24rpx 0 24rpx;
|
|||
|
|
box-shadow: 0 6rpx 20rpx rgba(0, 0, 0, 0.05);
|
|||
|
|
overflow: hidden;
|
|||
|
|
box-sizing: border-box;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 卡片样式 */
|
|||
|
|
.card-wrapper {
|
|||
|
|
background: #ffffff;
|
|||
|
|
border-radius: 20rpx;
|
|||
|
|
padding: 24rpx;
|
|||
|
|
margin: 20rpx 24rpx 0 24rpx;
|
|||
|
|
box-shadow: 0 6rpx 20rpx rgba(0, 0, 0, 0.05);
|
|||
|
|
overflow: hidden;
|
|||
|
|
box-sizing: border-box;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.card-wrapper-service {
|
|||
|
|
background: #ffffff;
|
|||
|
|
border-radius: 20rpx;
|
|||
|
|
margin: 20rpx 24rpx;
|
|||
|
|
box-shadow: 0 6rpx 20rpx rgba(0, 0, 0, 0.05);
|
|||
|
|
overflow: hidden;
|
|||
|
|
box-sizing: border-box;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.shop_box {
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
align-items: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 空数据状态 */
|
|||
|
|
.list-container ::v-deep .no-data {
|
|||
|
|
height: 100%;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
background: #f5f5f5;
|
|||
|
|
}
|
|||
|
|
.scroll-locked {
|
|||
|
|
// overflow: hidden !important;
|
|||
|
|
// height: 100vh !important;
|
|||
|
|
position: fixed !important;
|
|||
|
|
width: 100% !important;
|
|||
|
|
}
|
|||
|
|
</style>
|