|
|
@@ -10,6 +10,23 @@
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
+ <!-- 搜索框区域 -->
|
|
|
+ <view class="search-wrap">
|
|
|
+ <view class="search-bar">
|
|
|
+ <app-search-module
|
|
|
+ v-model="searchText"
|
|
|
+ :placeholder="isSearching ? '正在搜索...' : '搜索商品名称'"
|
|
|
+ @input="searchFn"
|
|
|
+ :height="66"
|
|
|
+ :fontSize="28"
|
|
|
+ />
|
|
|
+ </view>
|
|
|
+ <!-- 搜索状态提示 -->
|
|
|
+ <view v-if="isSearching" class="search-loading">
|
|
|
+ <text class="loading-text">正在搜索...</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
<view class="buy-material-container">
|
|
|
<button class="buy-material-btn" @click="buyProduct()">
|
|
|
<view class="btn-content">
|
|
|
@@ -19,13 +36,40 @@
|
|
|
</view>
|
|
|
|
|
|
<scroll-view scroll-y scroll-with-animation class="tab-view" :scroll-top="scrollTop">
|
|
|
- <view v-for="(item,index) in tabbar" :key="index" class="tab-bar-item" :class="[currentTab==index ? 'active' : '']" :data-current="index" @tap.stop="swichNav($event ,item)" >
|
|
|
+ <view v-for="(item,index) in tabbar" :key="index" class="tab-bar-item" :class="[!isSearchMode && currentTab==index ? 'active' : '']" :data-current="index" @tap.stop="swichNav($event ,item)" >
|
|
|
<text>{{item.categoryName}}</text>
|
|
|
</view>
|
|
|
<view style="height:150upx"></view>
|
|
|
</scroll-view>
|
|
|
+
|
|
|
+ <!-- 搜索结果展示区域 -->
|
|
|
+ <scroll-view v-if="isSearchMode" scroll-y class="right-box" @scrolltolower="reachBottom">
|
|
|
+ <view class="page-view" v-if="!$util.isEmpty(list.data)" >
|
|
|
+ <view class="goods-list" v-for="(items) in list.data" :key="items.id" @click="toBuy(items)" >
|
|
|
+ <view class="img-blo">
|
|
|
+ <image :src="items.smallCover" mode="aspectFill" ></image>
|
|
|
+ </view>
|
|
|
+ <view class="goods-det">
|
|
|
+ <view class="goods-det-top">
|
|
|
+ <view class="goods-name">{{ items.name }}</view>
|
|
|
+ <view class="goods-sales"></view>
|
|
|
+ </view>
|
|
|
+ <view class="goods-price">¥{{ items.price?parseFloat(items.price):0 }}</view>
|
|
|
+ <view class="buy-btn">购买</view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <block v-else>
|
|
|
+ <app-wrapper-empty
|
|
|
+ :title="getEmptyTitle()"
|
|
|
+ :is-empty="$util.isEmpty(list.data)"
|
|
|
+ />
|
|
|
+ </block>
|
|
|
+ </scroll-view>
|
|
|
+
|
|
|
+ <!-- 分类商品展示区域 -->
|
|
|
<block v-for="(item,index) in tabbar" :key="index" >
|
|
|
- <scroll-view scroll-y class="right-box" @scrolltolower="onReachBottoms" v-if="currentTab==index" >
|
|
|
+ <scroll-view scroll-y class="right-box" @scrolltolower="reachBottom" v-if="!isSearchMode && currentTab==index" >
|
|
|
<view class="page-view" v-if="!$util.isEmpty(list.data)" >
|
|
|
<view class="goods-list" v-for="(items) in list.data" :key="items.id" @click="toBuy(items)" >
|
|
|
<view class="img-blo">
|
|
|
@@ -42,13 +86,15 @@
|
|
|
</view>
|
|
|
</view>
|
|
|
<block v-else>
|
|
|
- <app-wrapper-empty title="暂无商品" :is-empty="$util.isEmpty(list.data)" />
|
|
|
+ <app-wrapper-empty
|
|
|
+ :title="getEmptyTitle()"
|
|
|
+ :is-empty="$util.isEmpty(list.data)"
|
|
|
+ />
|
|
|
</block>
|
|
|
</scroll-view>
|
|
|
</block>
|
|
|
|
|
|
<wangCg :loginStyle.sync="globalLoginStyle" @goToLogin="loginTo()"></wangCg>
|
|
|
-
|
|
|
</view>
|
|
|
</template>
|
|
|
<script>
|
|
|
@@ -56,6 +102,7 @@ import { mapGetters } from 'vuex'
|
|
|
import AppImg from '@/components/app-img'
|
|
|
import wangCg from "@/components/wangCg"
|
|
|
import AppWrapperEmpty from '@/components/app-wrapper-empty'
|
|
|
+import AppSearchModule from '@/components/item/module/app-search'
|
|
|
import { getClass, getList } from '@/api/category'
|
|
|
import { list,share } from '@/mixins'
|
|
|
import { getInfo } from "@/api/shop"
|
|
|
@@ -64,7 +111,8 @@ export default {
|
|
|
components: {
|
|
|
AppWrapperEmpty,
|
|
|
AppImg,
|
|
|
- wangCg
|
|
|
+ wangCg,
|
|
|
+ AppSearchModule
|
|
|
},
|
|
|
mixins: [list, share],
|
|
|
data () {
|
|
|
@@ -80,11 +128,16 @@ export default {
|
|
|
myClass: 'fadeIn',
|
|
|
shopName:'',
|
|
|
shopImg:'',
|
|
|
- hdId:0
|
|
|
+ hdId:0,
|
|
|
+ searchText: '', // 搜索文本
|
|
|
+ searchTimer: null, // 搜索防抖定时器
|
|
|
+ isSearchMode: false, // 是否为搜索模式
|
|
|
+ isSearching: false, // 是否正在搜索
|
|
|
+ lastSearchText: '' // 上次搜索的文本,用于优化
|
|
|
}
|
|
|
},
|
|
|
computed: {
|
|
|
- ...mapGetters({ loginInfo:"getLoginInfo" })
|
|
|
+ ...mapGetters({ loginInfo:"getLoginInfo" })
|
|
|
},
|
|
|
onShareAppMessage(res) {
|
|
|
return {
|
|
|
@@ -100,6 +153,14 @@ export default {
|
|
|
this.globalLoginStyle = 0
|
|
|
}
|
|
|
},
|
|
|
+ onUnload() {
|
|
|
+ // 清理搜索定时器
|
|
|
+ if (this.searchTimer) {
|
|
|
+ clearTimeout(this.searchTimer);
|
|
|
+ this.searchTimer = null;
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
watch: {
|
|
|
loginInfo: {
|
|
|
deep: true,
|
|
|
@@ -114,21 +175,21 @@ export default {
|
|
|
}
|
|
|
},
|
|
|
methods: {
|
|
|
- toBuy(item){
|
|
|
- let account = this.option.account?this.option.account:0
|
|
|
- this.$util.pageTo({url:"/pages/goods/detail?id="+item.id+'&hdId='+this.hdId+'&categoryId='+this.form.categoryId+'&account='+account})
|
|
|
- },
|
|
|
- buyProduct(){
|
|
|
- let account = this.option.account?this.option.account:0
|
|
|
- this.$util.pageTo({url:"/pages/item/item?account="+account+'&hdId='+this.hdId,type:2})
|
|
|
- },
|
|
|
- loginTo() {
|
|
|
- let account = this.option.account?this.option.account:0
|
|
|
- this.$util.pageTo({url:"/pages/login/index?needBack=1&account="+account})
|
|
|
- },
|
|
|
- init(){
|
|
|
- this.hdId = this.option.hdId ? this.option.hdId : 0
|
|
|
- this.classInit()
|
|
|
+ toBuy(item){
|
|
|
+ let account = this.option.account?this.option.account:0
|
|
|
+ this.$util.pageTo({url:"/pages/goods/detail?id="+item.id+'&hdId='+this.hdId+'&categoryId='+this.form.categoryId+'&account='+account})
|
|
|
+ },
|
|
|
+ buyProduct(){
|
|
|
+ let account = this.option.account?this.option.account:0
|
|
|
+ this.$util.pageTo({url:"/pages/item/item?account="+account+'&hdId='+this.hdId,type:2})
|
|
|
+ },
|
|
|
+ loginTo() {
|
|
|
+ let account = this.option.account?this.option.account:0
|
|
|
+ this.$util.pageTo({url:"/pages/login/index?needBack=1&account="+account})
|
|
|
+ },
|
|
|
+ init(){
|
|
|
+ this.hdId = this.option.hdId ? this.option.hdId : 0
|
|
|
+ this.classInit()
|
|
|
getInfo({hdId:this.hdId}).then(res=>{
|
|
|
if(res.code == 1){
|
|
|
if(res.data.info && res.data.info.merchantName){
|
|
|
@@ -148,19 +209,108 @@ export default {
|
|
|
}
|
|
|
})
|
|
|
},
|
|
|
- onReachBottoms () {
|
|
|
- if (!this.list.finished) {
|
|
|
- this.getGoodList().then(res => {
|
|
|
- uni.stopPullDownRefresh()
|
|
|
- });
|
|
|
+ // 搜索输入处理(防抖)
|
|
|
+ searchFn() {
|
|
|
+ console.log('searchFn----------', this.searchText, 'lastSearchText:', this.lastSearchText)
|
|
|
+
|
|
|
+ // 清除之前的定时器
|
|
|
+ if (this.searchTimer) {
|
|
|
+ clearTimeout(this.searchTimer)
|
|
|
+ this.searchTimer = null
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果搜索文本为空
|
|
|
+ if (this.$util.isEmpty(this.searchText)) {
|
|
|
+ // 如果之前也是空文本(首次点击或焦点清空),跳过搜索
|
|
|
+ if (this.$util.isEmpty(this.lastSearchText)) {
|
|
|
+ console.log('搜索文本为空且之前也为空,跳过搜索')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ // 如果之前有搜索文本,现在清空了,则恢复分类筛选
|
|
|
+ this.clearSearch()
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果搜索文本与上次相同,不重复搜索
|
|
|
+ if (this.searchText === this.lastSearchText) {
|
|
|
+ console.log('搜索文本未变化,跳过搜索')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置防抖定时器,1秒后执行搜索
|
|
|
+ this.searchTimer = setTimeout(() => {
|
|
|
+ this.performSearch()
|
|
|
+ }, 1000)
|
|
|
+ },
|
|
|
+
|
|
|
+ // 执行搜索
|
|
|
+ performSearch() {
|
|
|
+ if (this.$util.isEmpty(this.searchText)) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log('执行搜索:', this.searchText)
|
|
|
+
|
|
|
+ this.isSearchMode = true
|
|
|
+ this.isSearching = true
|
|
|
+ this.lastSearchText = this.searchText
|
|
|
+ this.resetList()
|
|
|
+ this.searchGoods()
|
|
|
+ },
|
|
|
+
|
|
|
+ // 清空搜索,恢复分类筛选
|
|
|
+ clearSearch() {
|
|
|
+ console.log('清空搜索,恢复分类筛选')
|
|
|
+ this.isSearchMode = false
|
|
|
+ this.isSearching = false
|
|
|
+ this.lastSearchText = ''
|
|
|
+
|
|
|
+ // 确保搜索框内容被清空
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.searchText = ''
|
|
|
+ })
|
|
|
+
|
|
|
+ this.resetList()
|
|
|
+ this.getGoodList()
|
|
|
+ },
|
|
|
+
|
|
|
+ // 搜索商品
|
|
|
+ searchGoods() {
|
|
|
+ return getList({
|
|
|
+ searchText: this.searchText,
|
|
|
+ page: this.list.page,
|
|
|
+ pageSize: 10
|
|
|
+ }).then(res => {
|
|
|
+ this.isSearching = false
|
|
|
+ this.completes(res)
|
|
|
+ console.log('搜索成功,数据已加载')
|
|
|
+ }).catch(err => {
|
|
|
+ this.isSearching = false
|
|
|
+ console.error('搜索失败:', err)
|
|
|
+ this.$msg('搜索失败,请重试')
|
|
|
+ })
|
|
|
+ },
|
|
|
+ reachBottom () {
|
|
|
+ console.log('reachBottom----------', this.list.finished)
|
|
|
+ if (this.list.finished == false && !this.isSearching) {
|
|
|
+ // 根据是否为搜索模式决定调用哪个方法
|
|
|
+ if (this.isSearchMode) {
|
|
|
+ this.searchGoods().then(res => {
|
|
|
+ uni.stopPullDownRefresh()
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ this.getGoodList().then(res => {
|
|
|
+ uni.stopPullDownRefresh()
|
|
|
+ });
|
|
|
+ }
|
|
|
} else {
|
|
|
uni.stopPullDownRefresh()
|
|
|
}
|
|
|
},
|
|
|
async classInit () {
|
|
|
this.getMyClass() .then(res => {
|
|
|
- this.getGoodList()
|
|
|
- })
|
|
|
+ this.getGoodList()
|
|
|
+ })
|
|
|
},
|
|
|
// 分类
|
|
|
getMyClass () {
|
|
|
@@ -173,18 +323,24 @@ export default {
|
|
|
},
|
|
|
// 列表
|
|
|
getGoodList () {
|
|
|
- return getList({ ...this.form, page: this.list.page,pageSize:100 }) .then(res => {
|
|
|
+ return getList({ ...this.form, page: this.list.page,pageSize:10 }) .then(res => {
|
|
|
+ // 处理商品列表数据,completes方法来自list混入
|
|
|
+ // 该方法会处理分页数据,更新list对象的data、page、finished等属性
|
|
|
+ // res包含接口返回的商品列表数据,completes方法会将数据添加到this.list.data中
|
|
|
this.completes(res)
|
|
|
})
|
|
|
},
|
|
|
// 点击标题切换当前页时改变样式
|
|
|
swichNav (e, item) {
|
|
|
let cur = e.currentTarget.dataset.current
|
|
|
- if (this.currentTab == cur) {
|
|
|
+ if (this.currentTab == cur && !this.isSearchMode) {
|
|
|
return false
|
|
|
} else {
|
|
|
this.currentTab = cur
|
|
|
this.form.categoryId = item.id
|
|
|
+
|
|
|
+ // 切换分类时清空搜索状态
|
|
|
+ this.clearSearchState()
|
|
|
this.resetList()
|
|
|
this.getGoodList()
|
|
|
}
|
|
|
@@ -203,6 +359,42 @@ export default {
|
|
|
},
|
|
|
tabClick (index) {
|
|
|
this.currentTabIndex = index
|
|
|
+ },
|
|
|
+
|
|
|
+ // 清空搜索状态(不触发数据重新加载)
|
|
|
+ clearSearchState() {
|
|
|
+ // 清除定时器
|
|
|
+ if (this.searchTimer) {
|
|
|
+ clearTimeout(this.searchTimer)
|
|
|
+ this.searchTimer = null
|
|
|
+ }
|
|
|
+
|
|
|
+ // 重置搜索相关状态
|
|
|
+ this.searchText = ''
|
|
|
+ this.isSearchMode = false
|
|
|
+ this.isSearching = false
|
|
|
+ this.lastSearchText = ''
|
|
|
+
|
|
|
+ // 确保搜索框内容被清空(强制更新)
|
|
|
+ // this.$nextTick(() => {
|
|
|
+ // this.searchText = ''
|
|
|
+ // })
|
|
|
+ },
|
|
|
+
|
|
|
+ // 获取空状态提示文字
|
|
|
+ getEmptyTitle() {
|
|
|
+ if (this.isSearchMode && this.searchText) {
|
|
|
+ return `未找到"${this.searchText}"相关商品`
|
|
|
+ }
|
|
|
+ return '暂无商品'
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 组件销毁时清理定时器
|
|
|
+ beforeDestroy() {
|
|
|
+ if (this.searchTimer) {
|
|
|
+ clearTimeout(this.searchTimer)
|
|
|
+ this.searchTimer = null
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -281,7 +473,7 @@ page {
|
|
|
position: fixed;
|
|
|
left: 0;
|
|
|
z-index: 10;
|
|
|
- height: calc(100vh - 100upx);
|
|
|
+ height: calc(100vh - 160upx); /* 调整高度以适应搜索框 */
|
|
|
background-color: #f0f2f6;
|
|
|
.tab-bar-item {
|
|
|
width: 200upx;
|
|
|
@@ -312,7 +504,7 @@ page {
|
|
|
.right-box {
|
|
|
width: 100%;
|
|
|
overflow: auto;
|
|
|
- height: calc(100vh - 220upx);
|
|
|
+ height: calc(100vh - 280upx); /* 调整高度以适应搜索框 */
|
|
|
padding-left: 220upx;
|
|
|
box-sizing: border-box;
|
|
|
.page-view {
|
|
|
@@ -361,10 +553,40 @@ page {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/* 搜索框样式 */
|
|
|
+.search-wrap {
|
|
|
+ padding: 20upx 30upx 10upx 30upx;
|
|
|
+ background: linear-gradient(to bottom, #3385FF, #fff);
|
|
|
+}
|
|
|
+
|
|
|
+.search-bar {
|
|
|
+ background-color: #fff;
|
|
|
+ border-radius: 30upx;
|
|
|
+ padding: 10upx;
|
|
|
+ box-shadow: 0upx 2px 8px 0upx rgba(0, 0, 0, 0.1);
|
|
|
+ position: relative;
|
|
|
+}
|
|
|
+
|
|
|
+.search-loading {
|
|
|
+ position: absolute;
|
|
|
+ right: 20upx;
|
|
|
+ top: 50%;
|
|
|
+ transform: translateY(-50%);
|
|
|
+ z-index: 100;
|
|
|
+
|
|
|
+ .loading-text {
|
|
|
+ font-size: 24upx;
|
|
|
+ color: #999;
|
|
|
+ background-color: rgba(255, 255, 255, 0.9);
|
|
|
+ padding: 5upx 10upx;
|
|
|
+ border-radius: 10upx;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
.buy-material-container {
|
|
|
position: absolute;
|
|
|
right: 20upx;
|
|
|
- top: 148upx;
|
|
|
+ top: 190upx; /* 调整位置以适应搜索框 */
|
|
|
z-index: 9999;
|
|
|
}
|
|
|
|