文章目錄
- 一、創建頁面
- 二、配置路由
- 三、寫接口文件(api)
- 1.定位的接口函數(騰訊地圖api)
- 實現代碼:
- 2.獲取場館分類的數據
- 3.獲取附近場館列表的數據
- 四、開發首頁頁面
- 1.頂部區域
- 2.搜索框
- 3.場館分類
- 4.附近場館列表
- 五、難點介紹
- 1.實時定位功能的實現
- 思路:
- 核心邏輯:
- 1)優先獲取精準定位:
- 2)緩存機制:
- 3)降級策略:
- 4)交互反饋:
- 實現代碼:
- 2.場館分類組件的實現
- 思路:
- 實現代碼:
- 3.附近場館列表組件的實現
- 思路:
- 實現代碼:
- 4.分頁加載
- 思路:
- 1)初始化狀態
- 2)首屏數據加載
- 3)滾動監聽觸發
- 4)分頁請求處理
- 5)邊界狀態管理
- 核心邏輯:
- 1)數據結構設計:
- 2)核心觸發機制:
- 3)細節:
- 4)分頁加載流程圖
- 實現代碼:
- 監聽用戶滑動到底部
- 請求下一頁的數據:
- 5.提升用戶體驗
- 1)骨架屏?:
- 2)回到頂部?:
- 3)錯誤提示?:
- 6.樣式與交互設計
- 六、思路和建議
- 項目說明和其他介紹:
一、創建頁面
在pages文件夾下創建index文件夾,下面添加index.vue頁面。
二、配置路由
在pages.json中配置首頁的信息
{"path": "pages/index/index","style": {// "navigationBarTitleText": "","navigationBarTitleText": "體育館預約系統","enablePullDownRefresh": false,// 網站類型"navigationStyle": "custom"}},
三、寫接口文件(api)
本項目的首頁需要寫關于以下幾個方面的接口函數
1.定位的接口函數(騰訊地圖api)
這里我根據騰訊位置服務中提供的一些接口,編寫地址的請求函數,主要是IP定位和逆地址解析。
官方文檔:
IP定位API文檔:
https://lbs.qq.com/service/webService/webServiceGuide/position/webServiceIp
逆地址解析API文檔:
https://lbs.qq.com/service/webService/webServiceGuide/address/Gcoder
實現代碼:
//IP定位
const IP = '111.206.145.41';
const API_KEY = '你的key';export function getLocationByIP(a) {a.$jsonp("https://apis.map.qq.com/ws/geocoder/v1/ip", {key: API_KEY,output: 'jsonp',// ip: IP, //要把這個ip這一行注釋掉// location: '31.973929,119.756208',//可以通過uni.getLocation獲取,谷歌瀏覽器會對定位請求清除,有時候定位準,有時候定位不準會出現初始地址甘肅省,但項目發布上https就行了,不準的時候用其他瀏覽器測試// location: '33.67,119.28',get_poi: '0'}).then(resp => {let res = resp;console.log(JSON.stringify(resp));let a = resp.result.ad_info;console.log(JSON.stringify(a));})
}
//逆地址解析
export async function reverseGeocoding(that, latitude, longitude) {try {const resp = await that.$jsonp("https://apis.map.qq.com/ws/geocoder/v1", {key: API_KEY,output: 'jsonp',location: `${latitude},${longitude}`,get_poi: '0'});return resp.result.formatted_addresses.recommend; // 明確返回 recommend} catch (error) {console.log("報錯啦");console.error('根據經緯度逆地址解析失敗:', error);throw error; // 重新拋出錯誤}
}
2.獲取場館分類的數據
export function getVenueTypes(keyword) {return httpRequest.request({url: '接口地址',method: 'GET',params: keyword})
}
3.獲取附近場館列表的數據
// 獲取場館列表
export function getVenueList(venueListReqDTO) {return httpRequest.request({url: '接口地址',method: 'post', data: venueListReqDTO})
}
四、開發首頁頁面
1.頂部區域
實時定位,icon小圖標
2.搜索框
3.場館分類
場館分類的組件(基礎實現和改進版本)
基礎版(使用u-scroll-list橫向滾動列表):
改進版(使用swiper實現滑動翻頁):
4.附近場館列表
場館列表的組件(該組件也可以在查詢頁面的場館列表渲染時復用)
五、難點介紹
1.實時定位功能的實現
思路:
開發者需要在騰訊位置服務先注冊一個賬號,然后選擇你想要的地圖相關功能,為這個功能分配一定的額度,個人開發者每天都有一定量的免費的額度,自己使用是足夠的了。下面是騰訊位置服務官網:
https://lbs.qq.com/location/
核心邏輯:
1)優先獲取精準定位:
這個項目主要使用了IP定位和逆地址解析兩個服務,或者為了更快獲取經緯度信息,還可以使用uni.getLocation獲取經緯度,這是uniapp的內置方法。成功獲取經緯度后,通過騰訊位置服務提供的逆地址解析功能,把經緯度信息解析為具體的地址,并顯示在頁面頂部的定位欄中。
2)緩存機制:
定位信息這里,還采用了緩存機制,將定位結果(經緯度)
在哪里查看緩存呢?如下圖所示,點擊應用程序,再展開本地存儲,就可以看到你的位置信息已經緩存起來了,這樣可以在你接下來再來訪問這個頁面的時候不用重新定位了,畢竟定位也需要重復請求花費一定的時間和額度。
代碼中還實現了基于用戶名的隔離緩存策略(避免多賬號沖突)
3)降級策略:
若用戶拒絕定位權限,嘗試通過 IP 定位獲取大致位置。
4)交互反饋:
定位過程中顯示“定位中…”,成功/失敗后更新地址欄,點擊地址欄可清空緩存重新定位。
實現代碼:
async getLocation() {this.isLocating = true; // 開始定位,設置狀態為定位中try {const res = await new Promise((resolve, reject) => {uni.getLocation({type: 'wgs84',success: (res) => {resolve(res);},fail: (err) => {reject(err);}});});this.locationInfo = {latitude: res.latitude,longitude: res.longitude,};console.log('當前位置的緯度:', res.latitude);console.log('當前位置的經度:', res.longitude);// 調用逆地址解析函數try {const recommend = await reverseGeocoding(this, res.latitude, res.longitude);// 更新推薦地址this.recommend = recommend;// 存儲到緩存const userName = uni.getStorageSync('curUser').userName;// console.log("userName:" + JSON.stringify(userName));const cacheKey = `location_${userName}`;let location = {latitude: res.latitude,longitude: res.longitude,recommend: recommend};console.log("location:" + JSON.stringify(location));uni.setStorageSync(cacheKey, location);console.log("逆地址解析成功,緩存鍵:", cacheKey);} catch (error) {console.error('逆地址解析失敗:', error);uni.showToast({title: '逆地址解析失敗',icon: 'none'});}} catch (err) {console.error('獲取位置失敗,嘗試通過 IP 獲取', err);try {const location = await getLocation();if (location) {this.locationInfo = {latitude: location.lat,longitude: location.lng};console.log('通過 IP 獲取的位置 - 緯度:', location.lat);console.log('通過 IP 獲取的位置 - 經度:', location.lng);} else {uni.showToast({title: '通過 IP 獲取位置失敗',icon: 'none'});}} catch (ipErr) {console.error('通過 IP 獲取位置失敗', ipErr);// uni.showToast({// title: '獲取位置失敗',// icon: 'none'// });}} finally {this.isLocating = false; // 定位結束,無論成功與否,都設置狀態為定位結束}},
2.場館分類組件的實現
思路:
可以使用u-scroll-list橫向滾動列表:
https://uviewui.com/components/scrollList.html#api
改進版使用swiper:
https://uniapp.dcloud.net.cn/component/swiper.html
實現代碼:
<!-- 設置 u-scroll-list 寬度為屏幕寬度 --><u-scroll-list direction="horizontal" :show-scrollbar="false" :enhanced="false" style="width: 100vw"><!-- 按每頁 10 個元素分組渲染 --><view class="page" v-for="(page, pageIndex) in groupedPages" :key="pageIndex"><view class="type-row" v-for="(row, rowIndex) in splitIntoRows(page)" :key="rowIndex"><view class="type-item" v-for="(item, index) in row" :key="index"><view class="icon-container"><text class="iconfont" v-html="item.icon"></text></view><text class="type-name">{{item.value}}</text></view></view></view></u-scroll-list>
<swiper class="swiper-container" :current="currentPage" :circular="false":display-multiple-items="1" :indicator-dots="false"><swiper-item v-for="(page, pageIndex) in groupedPages" :key="pageIndex"><view class="page"><view class="type-row" v-for="(row, rowIndex) in splitIntoRows(page)" :key="rowIndex"><view class="type-item" v-for="(item, index) in row" :key="index"><view class="icon-container"><text class="iconfont" v-html="item.icon"></text></view><text class="type-name":style="{ color: selectedType === item.value ? 'blue' : 'inherit' }">{{item.value}}</text></view></view></view></swiper-item></swiper>
3.附近場館列表組件的實現
思路:
1)將場館列表單獨封裝成組件,通過props接收數據。
2)用戶體驗:通過圖片懶加載、文字截斷處理(省略號)、開放時間分開顯示等美化組件的布局,提升用戶體驗。
實現代碼:
<!-- 場館列表 --><view class="venue-list"><view class="venue-row"><view class="venue-item" v-for="(item,index) in venueList" :key="index" @click="goToVenueDetail(item.id)"><!-- 圖片容器,添加加載效果 --><view class="image-container"><image class="venue-image":src="item.pictureList && item.pictureList.length > 0 ? urlConstruct(item.pictureList[0].url) : '{{item.url}}'"lazy-load="true" mode="aspectFill" @error="onImageError(index)"@load="onImageLoad(index)"></image><!-- 加載動畫 --><view class="image-loading" v-if="!imageLoaded[index]"><u-loading-icon mode="circle" color="#2979ff" size="24"></u-loading-icon></view></view><view class="venue-info"><view class="venue-name-tag"><view class="venue-name">{{truncateName(item.name)}}</view><view class="venue-tags"><text class="tag">{{item.typeName}}</text></view></view><view class="venue-meta"><view class="map-distance"><u-icon name="map" color="#666" size="13"></u-icon><text>{{item.distance ? parseFloat(item.distance).toFixed(1) : '0.0'}}km</text><text class="text-ellipsis">{{item.address}}</text></view></view><view class="venue-contact"></view><view class="venue-hours"><view class="icon-text-container"><u-icon name="clock" color="#666" size="12"></u-icon><span style="margin-left: 3px;">{{truncateOpenTimeFirstLine(item.openTime)}}</span></view><span class="remaining-open-time">{{truncateOpenTimeRemaining(item.openTime)}}</span></view></view></view></view></view>
4.分頁加載
思路:
1)初始化狀態
2)首屏數據加載
3)滾動監聽觸發
4)分頁請求處理
5)邊界狀態管理
數據加載完畢的判定和異常錯誤處理
核心邏輯:
1)數據結構設計:
-
venueListData.data
存儲分頁數據(包含 current/size/total/records 字段) -
page
對象維護當前頁碼(pageNum)和分頁大小(pageSize) -
loadmoreStatus
控制加載狀態(loadmore/loading/nomore/error)
2)核心觸發機制:
-
通過onReachBottom生命周期監聽滾動到底部事件
-
滾動位置通過onPageScroll實時更新,用于控制返回頂部按鈕
3)細節:
-
頁碼計算采用
current = pageNum - 1
的轉換邏輯(適配后端0-based分頁) -
使用數組合并策略:
records = [...oldRecords, ...newRecords]
-
雙重狀態判斷(records.length >= total 和 API響應空數據)
4)分頁加載流程圖
實現代碼:
監聽用戶滑動到底部
// 監聽用戶滑動到底部onReachBottom() {this.getMoreVenueList();console.log('頁面滾動到底部,觸發分頁加載');},watch: {loadmoreStatus(newStatus) {console.log('loadmoreStatus 發生變化,新值為:', newStatus);if (newStatus === 'loadmore') {console.log('分頁加載成功');} else if (newStatus === 'nomore') {console.log('分頁加載無新數據');} else if (newStatus === 'error') {console.log('分頁加載失敗');}}},
請求下一頁的數據:
/*** 發起場館列表請求*/async fetchVenueList() {try {return await getVenueList({current: this.page.pageNum - 1,size: this.page.pageSize,latitude: this.locationInfo.latitude,longitude: this.locationInfo.longitude,km: 10,});if (!response.data || !response.data.records || response.data.records.length === 0) {console.error('獲取場館列表數據為空');this.dataLoadError = true;this.loading = false;throw new Error('獲取場館列表數據為空');}return response;} catch (error) {console.error('獲取場館列表數據失敗:', error);this.loading = false; // 隱藏骨架屏throw error;}},
/*** 獲取下一頁的場館信息*/async getMoreVenueList() {if (this.venueListData.data.records.length >= this.total) {// 沒有更多數據了this.loadmoreStatus = "nomore";} else {if (!this.loading) {this.page.pageNum++;// 顯示正在加載this.loadmoreStatus = "loading";// 修改后try {const newData = await this.fetchVenueList();this.venueListData.data.records = this.venueListData.data.records.concat(newData.data.records);this.loadmoreStatus = newData.data.records.length > 0 ? "loadmore" : "nomore";} catch (error) {console.error('獲取下一頁場館列表數據失敗:', error);this.loadmoreStatus = "error";this.loading = false; // 隱藏骨架屏this.loadmoreStatus = "error";}}}},
5.提升用戶體驗
1)骨架屏?:
數據加載前顯示骨架屏,骨架屏與真實布局高度一致,避免空白頁帶來的視覺焦慮。
代碼實現:
<!-- 骨架屏結構與真實場館列表保持DOM結構一致 -->
<u-skeleton avatarSize="88" // 匹配場館封面圖尺寸rows="2" // 模擬描述文字行數rowsWidth="90%" // 模擬文字長度:animate="true" // 呼吸動畫減少等待焦慮
/>
2)回到頂部?:
滾動時顯示 u-back-top
按鈕,優化長列表瀏覽。
template:
<!-- 回到上方按鈕 --><u-back-top :scroll-top="scrollTop"></u-back-top>
script
// 用來控制滾動到最上方,在data(){}中設置scrollTop: 0,
// 在滑動過程實時獲取現在的滾動條位置,并保存當前的滾動條位置onPageScroll(e) {this.scrollTop = e.scrollTop;},
3)錯誤提示?:
通過 u-toast
顯示操作反饋(如生成數據成功提示)。
6.樣式與交互設計
-
響應式布局?:通過 Flex 布局適配不同屏幕尺寸。
-
動效反饋?:骨架屏動畫、按鈕點擊態(
:active
樣式)提升操作感。
六、思路和建議
在首頁的搭建過程中可以采用從上到下的搭建方式,從頂部位置信息欄開始,到搜索框,再到場館分類,附近場館列表。
思路上要注意,由于附近場館列表的信息中有相關位置信息,所以這里的邏輯是需要定位完成才可以顯示,所以要先進行定位,然后才能通過定位信息進一步展示出附近場館信息,這里附近的范圍為10km。也就是說,如果你附近10km沒有場館,附近場館列表就沒有數據顯示,這進一步說明了先進行定位是必要的。
建議:定位功能可以使用瀏覽器自帶的,也可以使用騰訊地圖,谷歌地圖等的api,當然在使用前,你需要看一下這個定位功能是否在你想展示的平臺都兼容,比如說你想要做一個網頁的平臺,你選擇的定位功能必須要在瀏覽器上兼容;如果你想做小程序,你就必須選擇能和你的小程序(如微信小程序、支付寶小程序、抖音小程序等)能夠兼容的定位功能。
項目說明和其他介紹:
SaaS場快訂平臺項目說明【持續更新】-CSDN博客
具體代碼可以查看相關開源倉庫,項目介紹視頻可見:
場快訂高并發場館預訂平臺開源啦,我的第一個開源項目歡迎大家多多支持!_嗶哩嗶哩_bilibili
完整的開源說明請見:
場快訂場館預定平臺開源說明-CSDN博客
感謝你的支持,希望我的文章對你有所幫助!