Files
hhrs/pages/venue/venue.vue
T

272 lines
5.8 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="page">
<!-- 搜索栏 -->
<view class="search-area">
<view class="nav-row">
<view class="back-btn" @click="goBack">
<u-icon name="arrow-left" color="#333333" size="40"></u-icon>
<text class="back-text">返回</text>
</view>
<view class="search-bar">
<u-icon name="search" color="#8e8e93" size="36"></u-icon>
<input class="search-input" v-model="keyword" placeholder="请搜索场馆名" confirm-type="search" @confirm="onSearch" />
<view class="search-btn" @click="onSearch">搜索</view>
</view>
</view>
</view>
<!-- 场馆列表 -->
<view class="list-area">
<view class="venue-card" v-for="item in list" :key="item.shopId" @click="goDetail(item)">
<image class="card-img" :src="item.image" mode="aspectFill" />
<view class="card-info">
<text class="card-name">{{ item.name }}</text>
<text class="card-category">{{ item.category }}</text>
<text class="card-address">{{ item.address }}</text>
</view>
<view class="card-distance" v-if="item.distance !== null">
<u-icon name="map" color="#999999" size="24"></u-icon>
<text class="distance-text">{{ item.distance < 1 ? Math.round(item.distance * 1000) + 'm' : Math.round(item.distance) + 'km' }}</text>
</view>
</view>
<view v-if="hasScrolled" class="nomore-area">
<u-loadmore :status="loadStatus" :load-text="{ loadmore: '', loading: '加载中...', nomore: '我也是有底线的' }" />
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
keyword: '',
list: [],
pageNo: 1,
pageSize: 10,
total: 0,
loading: false,
lng: '',
lat: '',
loadStatus: 'loadmore',
hasScrolled: false,
reqSeq: 0
}
},
onLoad() {
const loc = uni.getStorageSync('lng') && uni.getStorageSync('lat')
? { lng: uni.getStorageSync('lng'), lat: uni.getStorageSync('lat') }
: null
if (loc) {
this.lng = loc.lng
this.lat = loc.lat
}
this.loadList()
},
// xuchao: 改用页面级触底加载(scroll-view 无固定高度时 scrolltolower 永不触发)
onReachBottom() {
this.loadMore()
},
methods: {
loadList() {
// xuchao: 请求序号 —— 新请求直接取代在途请求,过期响应整体丢弃(修复搜索竞态,故不再用 loading 提前 return)
const seq = ++this.reqSeq
this.loading = true
this.loadStatus = 'loading'
const params = {
pageNo: this.pageNo,
pageSize: this.pageSize,
keyword: this.keyword,
typeId: 1
}
if (this.lng) params.lng = this.lng
if (this.lat) params.lat = this.lat
this.$u.api.getStoreList(params).then(res => {
if (seq !== this.reqSeq) return
if (res.errcode === 0 && res.data) {
const list = res.data.list || []
if (this.pageNo === 1) {
this.list = list
} else {
this.list = this.list.concat(list)
}
this.total = res.data.total || 0
// xuchao: 本页不足一页即已到尾部,钉住 total,防止 total 中途变大时空翻
if (list.length < this.pageSize) this.total = this.list.length
}
}).finally(() => {
if (seq !== this.reqSeq) return
this.loading = false
if (this.list.length === 0) {
this.loadStatus = 'nomore'
} else if (this.list.length >= this.total) {
this.loadStatus = 'nomore'
} else {
this.loadStatus = 'loadmore'
}
})
},
loadMore() {
this.hasScrolled = true
if (this.list.length < this.total && !this.loading) {
this.pageNo++
this.loadList()
}
},
goBack() {
// xuchao: 直接通过链接进入(无历史栈)时回到首页
const pages = getCurrentPages()
if (pages.length > 1) {
uni.navigateBack()
} else {
uni.reLaunch({ url: '/pages/index/index' })
}
},
goDetail(item) {
uni.navigateTo({ url: '/pages/venue/detail?id=' + item.shopId })
},
onSearch() {
this.pageNo = 1
this.list = []
this.loadList()
}
}
}
</script>
<style>
page {
background-color: #f2f2f7;
}
</style>
<style scoped lang="less">
.search-area {
background-color: #ffffff;
padding: 6px 12px;
/* xuchao: 滚动时固定在页面顶部 */
position: sticky;
top: 0;
z-index: 10;
}
.nav-row {
display: flex;
align-items: center;
gap: 12px;
}
.back-btn {
display: flex;
align-items: center;
justify-content: center;
height: 44px;
flex-shrink: 0;
gap: 2px;
}
.back-text {
font-size: 14px;
color: #333333;
}
.search-bar {
flex: 1;
display: flex;
align-items: center;
height: 44px;
background-color: #ffffff;
border: 1px solid #f2f2f7;
border-radius: 100px;
padding: 4px 8px;
gap: 12px;
}
.search-input {
flex: 1;
font-size: 16px;
color: #333;
}
.search-btn {
background-color: #0088ff;
color: #ffffff;
font-size: 14px;
padding: 4px 10px;
border-radius: 50px;
white-space: nowrap;
}
.list-area {
padding: 12px;
}
.venue-card {
display: flex;
align-items: flex-end;
background-color: #ffffff;
border: 1px solid #f2f2f7;
border-radius: 6px;
width: 702rpx;
padding: 24px 12px;
gap: 12px;
box-shadow: 0 2px 3px #f2f2f7;
margin-bottom: 12px;
}
.card-img {
width: 75px;
height: 75px;
border-radius: 6px;
flex-shrink: 0;
}
.card-info {
flex: 1;
display: flex;
flex-direction: column;
gap: 0;
}
.card-name {
font-size: 16px;
font-weight: 500;
color: #000000;
}
.card-category {
font-size: 14px;
color: #727272;
margin-top: 8px;
/* xuchao: 分类为空也预留一行高度,防止卡片内容底部对齐时名称上下跳动 */
min-height: 20px;
line-height: 20px;
}
.card-address {
font-size: 14px;
color: #727272;
margin-top: 8px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 200px;
}
.card-distance {
display: flex;
align-items: center;
gap: 2px;
flex-shrink: 0;
}
.distance-text {
font-size: 12px;
color: #727272;
}
.nomore-area {
padding-top: 24px;
}
</style>