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

741 lines
18 KiB
Vue
Raw Permalink 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>
<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>