mrr.sj.front/pages/search/search-result.vue

1020 lines
30 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="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%, #FF4767 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: #FF4767;
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, #FF4767 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 #FF4767;
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>