mrr.sj.front/pages/selfOperated/components/select-store.vue

741 lines
18 KiB
Vue
Raw Normal View History

2026-03-24 11:45:13 +08:00
<template>
<view>
<view class="select-store" v-if="sjList.length ">
<view class="flex-row-center-between">
<text class="select-store-title">适用门店</text>
<view class="select-store-right flex-row-center" @click="open">
<text class="select-store-right-text">{{ clickItem?"切换门店":"请选择门店" }}</text>
<image class="select-store-right-img" src="/static/images/wallet/right.png"></image>
</view>
</view>
<view class="store-info" v-if="clickItem">
<view class="info-header">
<image class="info-header-left" :src="clickItem.head_photo"></image>
<view class="info-header-right">
<view class="info-header-right-name flex-row-center-between">
<text class="right-name-text">{{ clickItem.name }}</text>
<view class="right-name-distance flex-row-center">
<image class="right-name-distance-icon" src="/static/images/icons/address_icon2.png">
</image>
<text class="right-name-distance-text">{{ clickItem.distance.toFixed(2) }}km</text>
</view>
</view>
<view style="display:flex;flex-direction:row">
<text class="info-header-right-time">
营业时间
</text>
<text class="info-header-right-time" style="color: #333;">{{ clickItem.business_time }}</text>
</view>
</view>
</view>
<view class="info-bottom">
<view class="info-bottom-left">
<text class="info-bottom-left-address">地址{{ clickItem.address }}</text>
<text class="info-bottom-left-phone">电话{{ clickItem.account}}</text>
</view>
<image class="right-adress-icon" src="/static/images/icons/adress.png" @click="openMap(clickItem)">
</image>
<image class="right-phone-icon" src="/static/images/icons/phone.png"
@click="contactService(clickItem.account)"></image>
</view>
</view>
</view>
<image v-else-if="!sjList.length && firstLoad" class="store-tip" src="/static/images/background/storeTip2.png" />
<uv-popup ref="popup" mode="bottom" bgColor="#f4f4f4">
<view class="store-list">
<text class="store-list-title">门店选择 </text>
<image class="store-list-close" src="/static/images/wallet/close.png" @click="close"></image>
<image class="store-list-tip" src="/static/images/background/storeTip.png"></image>
<scroll-view scroll-y style="height: 800rpx" @scrolltolower="loadMore">
<view class="store-list-item" v-for="(item, index) in sjList" :key="index">
<view class="item-container" @click="clickSj(item)">
<image class="item-container-left" :src="item.head_photo"></image>
<view class="item-container-right">
<view class="item-container-right-name flex-row-center-between">
<text class="right-name-text">{{ item.name }}</text>
<view class="right-name-distance flex-row-center">
<image class="right-name-distance-icon"
src="/static/images/icons/address_icon2.png"></image>
<text class="right-name-distance-text">{{ item.distance.toFixed(2) }}km</text>
</view>
</view>
<text class="item-container-right-time">
营业时间{{ item.business_time }}
</text>
<view class="item-container-right-adress flex-row-center">
<text class="right-adress-text">{{ item.address }}</text>
<image class="right-adress-icon" src="/static/images/icons/adress.png"
@click="openMap(item)"></image>
</view>
<view class="item-container-right-phone flex-row-center">
<text class="right-phone-text">电话{{ item.account }}</text>
<image class="right-phone-icon" src="/static/images/icons/phone.png"
@click="contactService(item.account)"></image>
</view>
</view>
</view>
<view class="item-types flex-row-center">
<text class="item-types-title">主营服务:</text>
<scroll-view scroll-x class="item-types-scroll">
<text class="item-type" v-for="(server, idx) in item.servers_kill_arr" :key="idx">
{{ server }}
</text>
</scroll-view>
</view>
</view>
<!-- 加载提示无数据时显示空提示 -->
<text class="empty-tip" v-if="sjList.length === 0 && !loading">
暂无相关门店
</text>
<view class="load-more-tip" v-if="sjList.length > 0">
<text class="load-more-tip-text" v-if="loading">加载中...</text>
<text class="load-more-tip-text" v-else-if="noMore">没有更多门店了</text>
</view>
</scroll-view>
</view>
</uv-popup>
</view>
</template>
<script>
import request from '@/utils/request'
import _public from "@/utils/public";
import locationService from "@/utils/locationService";
import permissionUtils from "@/utils/per";
export default {
props: {
id: {
type: [Number, String],
default: "",
},
src: {
type: String,
default: "/user/serversSelf/sjList",
},
},
data() {
return {
firstLoad: false,// 是否首次加载
clickItem: null,
sjList: [],
queryData: {
id: "",
userLngz: "117.333333",
userLat: "38.366667",
page: 1,
limit: 5,
},
loading: false, // 加载中状态
noMore: false, // 没有更多数据
totalCount: 0 // 总条数新增存储接口返回的count
};
},
computed: {
reservationAddress() {
return this.$store.state.reservationAddress;
},
reservationSyr() {
return this.$store.state.reservationSyr;
},
reservationTime() {
return this.$store.state.reservationTime;
},
userAdrees(){
return uni.getStorageSync("userAdrees").addressRes || getApp().globalData.addressRes;
}
},
watch: {
id(newVal) {
if (newVal) {
this.resetPagination();
this.getSjList(newVal);
}
},
},
created() {
this.getLocation();
this.queryData.city = this.userAdrees.cCode
if (this.id) {
this.getSjList(this.id);
}
},
methods: {
clickSj(item) {
this.clickItem = item;
this.$store.commit("setSelectStore", item);
this.$emit("select-store", item);
this.close();
},
getLocation() {
let userAdrees = uni.getStorageSync("userAdrees");
if (userAdrees) {
let addressRes = userAdrees.addressRes;
this.queryData.userLat = addressRes.latitude;
this.queryData.userLngz = addressRes.longitude;
}
},
// 重置分页状态新增重置totalCount
resetPagination() {
this.queryData.page = 1;
this.sjList = [];
this.loading = false;
this.noMore = false;
this.totalCount = 0; // 切换id时重置总条数
},
// 获取门店列表核心优化根据totalCount判断是否有更多数据
getSjList(id) {
if (id) {
this.queryData.id = id;
}
// 防止重复请求 + 没有更多数据时不请求
if (this.loading || this.noMore) return;
this.loading = true;
request
.post(this.src, this.queryData)
.then((res) => {
if (res.code == 200) {
const {
list = [], count = 0
} = res.data; // 解构list和count
this.totalCount = count; // 保存总条数
// 第一页覆盖,后续页追加
if (this.queryData.page === 1) {
this.sjList = list;
} else {
this.sjList = this.sjList.concat(list);
}
// 核心优化:根据总条数判断是否还有更多数据
// 逻辑:当前已加载条数 >= 总条数 → 没有更多
this.noMore = this.sjList.length >= this.totalCount;
// 额外处理总条数为0时显示空提示
if (this.totalCount === 0) {
this.noMore = true;
}
} else {
uni.showToast({
title: res.msg || "获取门店失败",
icon: "none",
});
// 请求失败回退页码
if (this.queryData.page > 1) {
this.queryData.page--;
}
}
})
.catch((err) => {
console.log('获取门店列表失败:', err);
if (this.queryData.page > 1) {
this.queryData.page--;
}
uni.showToast({
title: "网络错误,请稍后重试",
icon: "none",
});
})
.finally(() => {
this.firstLoad = true;
this.loading = false;
});
},
// 滚动加载更多(优化:判断当前页码是否超过总页数)
loadMore() {
// 正在加载、没有更多数据、总条数为0时不执行
if (this.loading || this.noMore || this.totalCount === 0) return;
// 计算总页数:向上取整(总条数/每页条数)
const totalPage = Math.ceil(this.totalCount / this.queryData.limit);
// 当前页码 < 总页数 → 加载下一页
if (this.queryData.page < totalPage) {
this.queryData.page++;
this.getSjList();
} else {
// 已到最后一页直接设置noMore
this.noMore = true;
}
},
open() {
this.$refs.popup.open();
},
close() {
this.$refs.popup.close();
},
openMap(store) {
if (!store.latitude || !store.longitude) {
uni.showToast({
title: "该门店暂无定位信息",
icon: "none"
});
return;
}
// #ifdef MP-WEIXIN
uni.openLocation({
latitude: parseFloat(store.latitude),
longitude: parseFloat(store.longitude),
name: store.name,
address: store.address,
success: () => console.log("打开地图成功"),
fail: (err) => console.log("打开地图失败:", err)
});
// #endif
// #ifdef APP-PLUS
_public.goMap({
latitude: parseFloat(store.latitude),
longitude: parseFloat(store.longitude),
name: store.name,
address: store.address,
});
// #endif
},
async contactService(phone) {
if (!phone) {
uni.showToast({
title: "暂无联系电话",
icon: "none"
});
return;
}
const systemInfo = uni.getSystemInfoSync();
if (systemInfo.platform === "ios") {
uni.makePhoneCall({
phoneNumber: phone
});
} else {
try {
const {
granted
} = await permissionUtils.checkPermission(
"phone",
"需要拨打电话权限,方便您联系商家"
);
if (granted) {
uni.makePhoneCall({
phoneNumber: phone
});
return;
}
const confirm = await this.showPermissionDialog(
"拨打电话权限申请",
"我们需要获取拨打电话权限,以便您直接联系商家"
);
if (!confirm) return;
const result = await permissionUtils.requestPermission(
"phone",
"需要拨打电话权限,方便您联系商家"
);
if (result) {
uni.makePhoneCall({
phoneNumber: phone
});
} else {
locationService.openAppSettings();
}
} catch (err) {
console.log("联系商家失败:", err);
uni.showToast({
title: "操作失败,请稍后重试",
icon: "none"
});
}
}
},
showPermissionDialog(title, content) {
return new Promise((resolve) => {
uni.showModal({
title,
content,
confirmText: "去设置",
cancelText: "取消",
success: (res) => resolve(res.confirm)
});
});
}
},
};
</script>
<style lang="less">
.select-store {
margin: 20rpx 24rpx 20rpx 24rpx;
padding: 30rpx 20rpx;
background: #ffffff;
border-radius: 20rpx;
.select-store-title {
font-weight: 500;
font-size: 30rpx;
color: #333333;
line-height: 42rpx;
text-align: left;
font-style: normal;
}
.select-store-right {
.select-store-right-text {
font-weight: 400;
font-size: 28rpx;
color: #666666;
line-height: 40rpx;
text-align: left;
font-style: normal;
}
.select-store-right-img {
width: 12rpx;
height: 24rpx;
margin-left: 20rpx;
}
}
}
.store-tip{
width: 702rpx;
margin: 0 29rpx;
height: 82rpx;
margin-bottom: 20rpx;
}
.store-info {
margin-top: 30rpx;
.info-header {
display: flex;
flex-direction: row;
padding-bottom: 20rpx;
margin-bottom: 20rpx;
border-bottom: 1rpx solid #F1F1F1;
.info-header-left {
width: 90rpx;
height: 90rpx;
border-radius: 18rpx;
object-fit: cover;
}
.info-header-right {
margin-left: 20rpx;
flex: 1;
.info-header-right-name {
.right-name-text {
font-weight: 500;
font-size: 28rpx;
color: #333333;
line-height: 40rpx;
text-align: left;
font-style: normal;
}
.right-name-distance {
.right-name-distance-text {
font-family: DINPro;
font-weight: 500;
font-size: 24rpx;
color: #666666;
line-height: 31rpx;
text-align: left;
font-style: normal;
margin-left: 6rpx;
}
.right-name-distance-icon {
width: 19rpx;
height: 21rpx;
}
}
}
.info-header-right-time {
font-weight: 400;
font-size: 24rpx;
color: #666666;
line-height: 33rpx;
text-align: left;
font-style: normal;
margin-top: 17rpx;
}
}
}
.info-bottom {
display: flex;
flex-direction: row;
align-items: center;
.info-bottom-left {
flex: 1;
.info-bottom-left-address {
font-weight: 400;
font-size: 24rpx;
color: #666666;
line-height: 33rpx;
text-align: left;
font-style: normal;
lines:1;
text-overflow: ellipsis;
width: 500rpx;
}
.info-bottom-left-phone {
font-weight: 400;
font-size: 24rpx;
color: #666666;
line-height: 33rpx;
text-align: left;
font-style: normal;
}
}
.right-adress-icon {
width: 43rpx;
height: 43rpx;
}
.right-phone-icon {
margin-left: 40rpx;
width: 43rpx;
height: 43rpx;
}
}
}
.store-list {
width: 750rpx;
background: #f4f4f4;
border-radius: 20rpx 20rpx 0rpx 0rpx;
padding: 30rpx 20rpx 0 20rpx;
position: relative;
.store-list-title {
font-weight: 500;
font-size: 32rpx;
color: #333333;
line-height: 45rpx;
text-align: center;
font-style: normal;
}
.store-list-close {
position: absolute;
top: 40rpx;
right: 40rpx;
width: 24rpx;
height: 24rpx;
}
.store-list-tip {
width: 376rpx;
height: 64rpx;
margin-top: 20rpx;
}
.store-list-item {
padding: 24rpx 20rpx;
background: #ffffff;
border-radius: 20rpx;
margin-bottom: 20rpx;
.item-container {
display: flex;
flex-direction: row;
.item-container-left {
width: 140rpx;
height: 140rpx;
border-radius: 24rpx;
object-fit: cover;
}
.item-container-right {
flex: 1;
margin-left: 20rpx;
.item-container-right-name {
.right-name-text {
font-weight: 500;
font-size: 28rpx;
color: #333333;
line-height: 32rpx;
text-align: left;
font-style: normal;
}
.right-name-distance {
.right-name-distance-text {
font-family: DINPro;
font-weight: 500;
font-size: 24rpx;
color: #666666;
line-height: 31rpx;
text-align: left;
font-style: normal;
margin-left: 6rpx;
}
.right-name-distance-icon {
width: 19rpx;
height: 21rpx;
}
}
}
.item-container-right-time {
font-weight: 400;
font-size: 24rpx;
color: #666666;
line-height: 33rpx;
text-align: left;
font-style: normal;
margin-top: 4rpx;
}
.item-container-right-adress {
margin-top: 4rpx;
.right-adress-text {
font-weight: 400;
font-size: 24rpx;
color: #666666;
line-height: 33rpx;
text-align: left;
font-style: normal;
// width: 460rpx;
max-width: 460rpx;
lines:1;
text-overflow: ellipsis;
}
.right-adress-icon {
width: 35rpx;
height: 35rpx;
margin-left: 14rpx;
}
}
.item-container-right-phone {
margin-top: 4rpx;
.right-phone-text {
font-weight: 400;
font-size: 24rpx;
color: #666666;
line-height: 33rpx;
text-align: left;
font-style: normal;
// width: 460rpx;
lines:1;
text-overflow: ellipsis;
}
.right-phone-icon {
width: 35rpx;
height: 35rpx;
margin-left: 14rpx;
}
}
}
}
.item-types {
margin-top: 14rpx;
display: flex;
flex-direction: row;
align-items: center;
.item-types-title {
font-weight: 400;
font-size: 24rpx;
color: #666666;
line-height: 33rpx;
text-align: left;
font-style: normal;
margin-right: 10rpx;
text-overflow: ellipsis;
}
.item-types-scroll {
white-space: nowrap;
flex: 1;
display: flex;
flex-direction: row;
}
.item-type {
margin: 0 14rpx;
font-weight: 400;
font-size: 20rpx;
color: #666666;
line-height: 32rpx;
text-align: center;
font-style: normal;
padding: 0 14rpx;
height: 32rpx;
background: #f3f3f3;
border-radius: 16rpx;
}
}
}
// 空数据提示样式
.empty-tip {
text-align: center;
padding: 80rpx 0;
font-size: 28rpx;
color: #999999;
background: #ffffff;
border-radius: 20rpx;
margin-bottom: 20rpx;
}
// 加载提示样式
.load-more-tip {
padding: 20rpx 0;
color: #999999;
// background: #ffffff;
border-radius: 20rpx;
margin-bottom: 20rpx;
.load-more-tip-text {
font-size: 24rpx;
text-align: center;
}
}
}
.flex-row-center-between {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
}
.flex-row-center {
display: flex;
flex-direction: row;
align-items: center;
}
</style>