mrr.sj.front/pages/shop/buy-service.vue

843 lines
18 KiB
Vue
Raw Permalink Normal View History

2026-03-24 11:45:13 +08:00
<template>
<view class="class-page" :class="{'popupShow':showFilter}">
<!-- 顶部导航栏 -->
<custom-navbar title="购买服务" :showBack="true" backgroundColor="#fff"></custom-navbar>
<!-- 一级Tab -->
<scroll-view scroll-x class="main-tab-scroll" :show-scrollbar="false">
<view class="main-tabs">
<view v-for="(tab, idx) in mainTabs" :key="tab.id"
:class="['main-tab', { active: queryData.first_class_id == tab.id }]"
@click="getFirstlist(tab.id, tab.title, tab.ratio)">
<text class="tab-text">{{ tab.title }}</text>
</view>
</view>
</scroll-view>
<!-- 二级icon分类 -->
<scroll-view scroll-x class="icon-tab-scroll">
<view class="icon-tabs">
<view v-for="(item, idx) in iconTabs" :key="item.id" class="icon-tab"
:class="{ active: queryData.second_class_id == item.id }" @click="getSecondlist(item.id)">
<image :src="item.photo" class="icon-img" mode="aspectFill"></image>
<text class="icon-text">{{ item.title }}</text>
</view>
</view>
</scroll-view>
<view class="filter-bar">
<view class="filter-bar-cont">
<view class="filter-item" @click="toggleTimeFilter" :class="{ active: showTimeFilter }">
<text class="filter-item-text">服务时间</text>
<image src="/static/images/sq_icon.png" mode="aspectFit" class="sq-icon" v-if="!showTimeFilter">
</image>
<image src="/static/images/zk_icon.png" mode="aspectFit" class="sq-icon" v-else></image>
</view>
<view class="filter-item" @click="toggleFilter" :class="{ active: showFilter }">
<text class="filter-item-text">筛选</text>
<image src="/static/images/filter_icon.png" mode="aspectFit" class="filter-icon"></image>
</view>
</view>
<!-- 筛选弹框 showFilter-->
</view>
<!-- 遮罩层 - 更新遮罩层显示逻辑 -->
<view class="popup-mask" v-if="showTimeFilter" @click="closeAllPopups" @touchmove.prevent.stop>
</view>
<!-- 服务卡片宫格 -->
<view class="service-list" v-if="serverlist.length != 0">
<service-grid :services="serverlist" @service-click="goToDetail" />
<!-- 加载更多 -->
<view class="loading-more" v-if="hasMore">加载中...</view>
<view class="no-more" v-else>没有更多了</view>
</view>
<!-- 暂无数据 -->
<noData v-else></noData>
<!-- 筛选弹框 showFilter-->
<filterPopup ref="filterPopup" @change="filterChange" :filterListSelectLists="filterListSelectLists"
:filterList="filterList" @reset="resetFilter" @sure="confirmFilter">
<filterCard :info="filterDetailsLists[0]" @selectDetail="selectDetailPrice" :isSingle="true"
:infoDetailList="infoDetailLists[0]">
<view class="price-input">
<input type="number" v-model="queryData.start_price" placeholder="最低价" class="price-input-field" />
<text class="price-separator"></text>
<input type="number" v-model="queryData.end_price" placeholder="最高价" class="price-input-field" />
</view>
</filterCard>
<filterCard :info="filterDetailsLists[1]" @selectDetail="selectDetailSort" :infoDetailList="infoDetailLists[1]" :isSingle="true"> </filterCard
>
</filterPopup>
</view>
</template>
<script>
import request from "../../utils/request";
import filterPopup from "components/filter-popup/filter-popup.vue";
import filterCard from "components/filterCard/filterCard.vue";
import customNavbar from "@/components/custom-navbar/custom-navbar.vue";
import _public from "../../utils/public";
export default {
components: {
customNavbar,
filterPopup,
filterCard
},
data() {
return {
filterList: [{
id: 1,
title: "价格",
},
{
id: 2,
title: "排序",
},
],
mainTabs: [],
iconTabs: [],
iconTabIndex: 0,
dateTabs: ["今日可约", "近两日可约", "近三日可约"],
dateTabIndex: 0,
serverlist: [],
isRefreshing: false,
hasMore: true,
infoDetailLists: [], // 筛选列表选择项
filterListSelectLists: [], // 筛选列表选择状态
filterDetailsLists: [{
id: 2,
title: "价格",
details: [{
id: 1,
name: "0-10元",
value: "0-10",
},
{
id: 2,
name: "10-30元",
value: "10-30",
},
{
id: 3,
name: "30-50元",
value: "30-50",
},
{
id: 4,
name: "50-100元",
value: "50-100",
},
{
id: 5,
name: "100-300元",
value: "100-300",
},
{
id: 6,
name: "300+",
value: "300-",
},
],
},
{
id: 3,
title: "排序",
details: [{
id: 1,
name: " 销量最高",
value: "1",
},
{
id: 2,
name: "价格最高",
value: "2",
},
{
id: 3,
name: "价格最低",
value: "3",
},
],
},
],
showTimeFilter: false,
selectedTimeOption: null,
timeOptions: [{
text: "今日可约"
},
{
text: "近两日可约",
discount: "64.31%"
},
{
text: "近三日可约"
},
],
showFilter: false,
priceRanges: [{
id: 0,
startPrice: 0,
endPrice: 99,
},
{
id: 1,
startPrice: 100,
endPrice: 199,
},
{
id: 2,
startPrice: 200,
endPrice: 299,
},
{
id: 3,
startPrice: 300,
endPrice: 499,
},
{
id: 4,
startPrice: 500,
endPrice: null,
},
],
selectedPricesId: -1,
sortOptions: ["销量最高", "价格最高", "价格最低"],
selectedSort: -1,
typeOptions: ["到家", "到店"],
selectedType: -1,
selectedStars: [],
minPrice: "",
maxPrice: "",
queryData: {
2026-03-30 14:32:03 +08:00
servers_region: null,
2026-03-24 11:45:13 +08:00
page: 1,
limit: 10,
},
ratio: null,
};
},
async onLoad() {
const beforeShopInfo = await request.post("/user/getuser", {
type: 3
});
2026-03-30 14:32:03 +08:00
this.queryData.servers_region = beforeShopInfo.data.dependency
2026-03-24 11:45:13 +08:00
const nowShopInfo = await request.post("/sj/sjdetail", {
id: beforeShopInfo.data.id,
});
// 加载一级类目
await request.post("/sj/firstclass").then((res) => {
this.mainTabs = res.data;
if (res.data.length != 0) {
this.ratio = this.mainTabs[0].ratio;
this.queryData.first_class_id = this.mainTabs[0].id;
}
});
// 获取商家端服务折扣
this.refreshList();
this.getIconTabs();
},
onPullDownRefresh() {
console.log("触发下拉刷新");
this.onRefresh();
setTimeout(() => {
console.log("刷新完毕");
uni.stopPullDownRefresh();
}, 1000);
},
onReachBottom() {
this.loadMore();
},
methods: {
//处理当前列表
handleSelectLists(detail, id) {
if (detail.length > 0) {
this.$set(this.filterListSelectLists, id, true);
} else {
this.$set(this.filterListSelectLists, id, false);
}
console.log("当前选择的筛选列表", this.filterListSelectLists);
// this.filterListId = id;
},
selectDetailPrice(detail) {
if (detail[0]) {
let priceList = detail[0].split("-");
this.queryData.start_price = priceList[0];
this.queryData.end_price = priceList[1] || null;
} else {
this.queryData.start_price = null;
this.queryData.end_price = null;
}
this.handleSelectLists(detail, 1);
this.$set(this.infoDetailLists, 0, detail);
console.log("选择的价格区间", detail);
},
selectDetailSort(detail) {
this.handleSelectLists(detail, 2);
//
this.$set(this.infoDetailLists, 1, detail);
//赋予排序方式
this.queryData.ordersort = detail[0];
console.log("选择的排序方式", detail);
},
refreshList() {
request.post("/sj/getserverlist", this.queryData).then((res) => {
let data = res.data.map((server) => ({
...server,
server_price: _public.roundUpToTwoDecimals(
Number(server.server_price),
this.ratio
),
}));
this.serverlist = [...this.serverlist, ...data];
if (res.count == this.serverlist.length) {
this.hasMore = false;
} else {
this.hasMore = true;
this.queryData.page = this.queryData.page + 1;
}
uni.stopPullDownRefresh();
});
},
// 下拉刷新
onRefresh() {
this.isRefreshing = true;
this.serverlist = [];
this.queryData.page = 1;
this.refreshList();
},
// 加载更多
loadMore() {
if (!this.hasMore) return;
// TODO: 加载更多订单数据
this.refreshList();
},
getIconTabs() {
// 加载二级类目
request
.post("/sj/secondclass", {
id: this.queryData.first_class_id
})
.then((res) => {
this.iconTabs = res.data;
});
},
getFirstlist(id, title, ratio) {
this.queryData.first_class_id = id;
this.ratio = ratio;
this.title = title;
delete this.queryData.second_class_id;
this.serverlist = [];
this.queryData.page = 1;
this.refreshList();
this.getIconTabs();
},
getSecondlist(id) {
this.queryData.second_class_id = id;
this.serverlist = [];
this.queryData.page = 1;
this.refreshList();
},
filterChange(e) {
this.showFilter = e.show
if (this.showFilter) {
this.showTimeFilter = false;
}
},
toggleFilter() {
this.$refs.filterPopup.open();
// this.showFilter = !this.showFilter;
// if (this.showFilter) {
// this.showTimeFilter = false;
// }
},
toggleTimeFilter() {
this.showTimeFilter = !this.showTimeFilter;
if (this.showTimeFilter) {
this.showFilter = false;
}
},
togglePrice(item) {
this.selectedPricesId = item.id;
this.queryData.start_price = item.startPrice;
this.queryData.end_price = item.endPrice;
},
selectSort(index) {
this.selectedSort = index;
this.queryData.ordersort = index + 1;
},
selectType(index) {
this.selectedType = index;
this.queryData.server_kind = index + 1;
},
closeTimeFilter() {
this.showTimeFilter = false;
},
getDate(index) {
this.queryData.date = [];
//今天的时间
var day1 = new Date();
day1.setTime(day1.getTime());
var m1 = day1.getMonth() + 1;
if (m1 <= 9) {
m1 = "0" + m1;
}
var d1 = day1.getDate();
if (d1 <= 9) {
d1 = "0" + d1;
}
var s1 = day1.getFullYear() + "-" + m1 + "-" + d1;
//明天的时间
var day2 = new Date();
day2.setTime(day2.getTime() + 24 * 60 * 60 * 1000);
var m2 = day2.getMonth() + 1;
if (m2 <= 9) {
m2 = "0" + m2;
}
var d2 = day2.getDate();
if (d2 <= 9) {
d2 = "0" + d2;
}
var s2 = day2.getFullYear() + "-" + m2 + "-" + d2;
//后天的时间
var day3 = new Date();
day3.setTime(day3.getTime() + 24 * 60 * 60 * 1000 * 2);
var m3 = day3.getMonth() + 1;
if (m3 <= 9) {
m3 = "0" + m3;
}
var d3 = day3.getDate();
if (d3 <= 9) {
d3 = "0" + d3;
}
var s3 = day3.getFullYear() + "-" + m3 + "-" + d3;
if (index == 0) {
return (this.queryData.date = [`${s1}`]);
} else if (index == 1) {
return (this.queryData.date = [`${s1}`, `${s2}`]);
} else if (index == 2) {
return (this.queryData.date = [`${s1}`, `${s2}`, `${s3}`]);
}
},
async selectTimeOption(index) {
this.selectedTimeOption = index;
await this.getDate(index);
this.showTimeFilter = false;
this.serverlist = [];
this.queryData.page = 1;
// 这里可以添加筛选逻辑
this.refreshList();
},
goToDetail(item) {
uni.navigateTo({
url: `/pages/shop/service-detail?id=${item.id}`,
});
},
resetFilter() {
this.infoDetailLists = [];
this.filterListSelectLists = [];
this.selectedStars = [];
this.selectedSort = -1;
this.selectedPricesId = -1;
this.selectedType = -1;
// this.queryData.start_price = "";
// this.queryData.end_price = "";
delete this.queryData.start_price;
delete this.queryData.end_price;
delete this.queryData.ordersort;
delete this.queryData.server_kind;
this.confirmFilter()
},
confirmFilter() {
this.closeFilter();
this.serverlist = [];
this.queryData.page = 1;
console.log(this.queryData);
this.refreshList();
},
closeFilter() {
this.showFilter = false;
},
closeAllPopups() {
this.showFilter = false;
this.showTimeFilter = false;
},
},
};
</script>
<style scoped>
.class-page {
padding-bottom: 30rpx;
width: 750rpx;
}
.popupShow {
overflow: hidden;
position: fixed;
}
.main-tab-scroll {
position: sticky;
top: calc(var(--status-bar-height) + 88rpx);
z-index: 9999;
white-space: nowrap;
}
.icon-tab-scroll {
position: relative;
z-index: 1999;
}
.main-tabs {
display: flex;
flex-flow: row nowrap;
justify-content: flex-start;
align-items: center;
background: #fff;
border-bottom: 1rpx solid #f5f5f5;
padding: 0 24rpx;
height: 88rpx;
gap: 40rpx;
padding-bottom: 20rpx;
}
.main-tab {
line-height: 88rpx;
position: relative;
}
.tab-text {
font-size: 28rpx;
color: #666;
2026-03-30 14:32:03 +08:00
white-space: nowrap;
2026-03-24 11:45:13 +08:00
}
.main-tab.active .tab-text {
2026-06-02 11:39:23 +08:00
color: #FF4767;
2026-03-24 11:45:13 +08:00
font-weight: 500;
}
.main-tab.active::after {
content: "";
position: absolute;
left: 50%;
bottom: 0;
transform: translateX(-50%);
width: 32rpx;
height: 6rpx;
2026-06-02 11:39:23 +08:00
background: #FF4767;
2026-03-24 11:45:13 +08:00
border-radius: 3rpx;
}
.icon-tabs {
display: flex;
flex-flow: row wrap;
justify-content: flex-start;
align-items: center;
background: #fff;
padding: 24rpx 0 0 24rpx;
}
.icon-tab {
width: 110rpx;
display: flex;
flex-direction: column;
align-items: center;
margin-right: 32rpx;
margin-bottom: 32rpx;
}
.icon-img {
width: 96rpx;
height: 96rpx;
border-radius: 48rpx;
margin-bottom: 12rpx;
}
.icon-text {
font-size: 24rpx;
color: #3d3d3d;
}
.icon-tab.active .icon-text {
2026-06-02 11:39:23 +08:00
color: #FF4767;
2026-03-24 11:45:13 +08:00
}
.icon-tab.active .icon-img {
2026-06-02 11:39:23 +08:00
border: 2rpx solid #FF4767;
2026-03-24 11:45:13 +08:00
}
.service-list {
margin: 32rpx 24rpx;
}
/* 服务时间弹框样式 */
.time-filter-popup {
width: 750rpx;
background-color: #ffffff;
transform: translateY(-120%);
opacity: 0;
transition: all 0.3s ease;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
visibility: hidden;
border-radius: 0 0 20rpx 20rpx;
overflow: hidden;
z-index: 998;
position: absolute;
top: 66rpx;
left: 0;
right: 0;
}
.time-filter-popup.show {
transform: translateY(0);
opacity: 1;
visibility: visible;
}
.time-filter-content {
padding: 0;
}
.time-option {
display: flex;
justify-content: space-between;
align-items: center;
padding: 32rpx;
background-color: #ffffff;
position: relative;
}
.time-option:not(:last-child)::after {
content: "";
position: absolute;
left: 32rpx;
right: 32rpx;
bottom: 0;
height: 1rpx;
background-color: #eeeeee;
}
.time-option.active {
background-color: #fff5f6;
}
.time-option.active .time-text {
2026-06-02 11:39:23 +08:00
color: #FF4767;
2026-03-24 11:45:13 +08:00
font-weight: 500;
}
.time-text {
font-size: 28rpx;
color: #333333;
}
.time-discount {
font-size: 24rpx;
2026-06-02 11:39:23 +08:00
color: #FF4767;
2026-03-24 11:45:13 +08:00
padding: 4rpx 12rpx;
background-color: #fff5f6;
border-radius: 20rpx;
}
/* 筛选栏样式 */
.filter-bar {
position: relative;
z-index: 999;
overflow: hidden;
}
.filter-bar-cont {
height: 66rpx;
width: 750rpx;
padding: 0 24rpx;
display: flex;
flex-flow: row nowrap;
justify-content: space-between;
align-items: center;
background-color: #fff;
position: relative;
z-index: 999;
}
.filter-item {
display: flex;
flex-flow: row nowrap;
justify-content: center;
align-items: center;
margin-right: 40rpx;
position: relative;
}
.filter-item-text {
font-size: 28rpx;
color: #666;
}
.filter-item.active .filter-item-text {
2026-06-02 11:39:23 +08:00
color: #FF4767;
2026-03-24 11:45:13 +08:00
}
.sq-icon {
width: 16rpx;
height: 16rpx;
margin-left: 8rpx;
}
.filter-icon {
width: 32rpx;
height: 32rpx;
margin-left: 8rpx;
}
/* 筛选相关样式 */
.filter-popup {
width: 750rpx;
position: absolute;
top: 66rpx;
left: 0;
right: 0;
background-color: #ffffff;
z-index: 99;
padding: 32rpx 24rpx;
transform: translateY(-100%);
opacity: 0;
transition: all 0.3s;
visibility: hidden;
box-sizing: border-box;
}
.filter-popup.show {
transform: translateY(0);
opacity: 1;
visibility: visible;
}
.filter-section {
margin-bottom: 32rpx;
}
.section-title {
display: inline-block;
font-size: 28rpx;
color: #333333;
margin-bottom: 16rpx;
}
.price-tags,
.sort-tags,
.star-tags {
display: flex;
flex-wrap: wrap;
gap: 20rpx;
}
.price-tag,
.sort-tag,
.star-tag {
width: 220rpx;
height: 52rpx;
text-align: center;
line-height: 50rpx;
border-radius: 26rpx;
2026-06-02 11:39:23 +08:00
border: 1rpx solid #FF4767;
2026-03-24 11:45:13 +08:00
font-size: 24rpx;
color: #3d3d3d;
box-sizing: border-box;
}
.price-tag.active,
.sort-tag.active,
.star-tag.active {
2026-06-02 11:39:23 +08:00
background-color: #FF4767;
2026-03-24 11:45:13 +08:00
color: #ffffff;
2026-06-02 11:39:23 +08:00
border-color: #FF4767;
2026-03-24 11:45:13 +08:00
}
.price-input {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 24rpx;
}
.price-input-field {
width: 220rpx;
height: 52rpx;
line-height: 50rpx;
text-align: center;
2026-06-02 11:39:23 +08:00
border: 1rpx solid #FF4767;
2026-03-24 11:45:13 +08:00
border-radius: 26rpx;
font-size: 24rpx;
color: #3d3d3d;
}
.price-separator {
margin: 0 16rpx;
color: #666666;
}
.filter-buttons {
display: flex;
gap: 34rpx;
margin-top: 48rpx;
}
.reset-btn,
.confirm-btn {
flex: 1;
height: 64rpx;
border-radius: 32rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 28rpx;
}
.reset-btn {
background-color: #ffffff;
2026-06-02 11:39:23 +08:00
border: 1rpx solid #FF4767;
color: #FF4767;
2026-03-24 11:45:13 +08:00
}
.confirm-btn {
2026-06-02 11:39:23 +08:00
background-color: #FF4767;
2026-03-24 11:45:13 +08:00
color: #ffffff;
}
/* 加载更多 */
.loading-more,
.no-more {
text-align: center;
font-size: 26rpx;
color: #999999;
padding: 24rpx 0;
}
/* 暂无数据 */
.service-nothings {
display: flex;
flex-flow: row nowrap;
justify-content: center;
align-items: center;
padding: 280rpx 0 0;
}
.nothings-text {
font-size: 32rpx;
font-weight: 500;
color: #999999;
text-align: center;
font-style: italic;
}
</style>