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