423 lines
13 KiB
Vue
423 lines
13 KiB
Vue
<template>
|
||
<view class="merchantList">
|
||
<view class="searchBox">
|
||
<view class="searchBox_left">
|
||
<image
|
||
src="https://wechat-img-file.oss-cn-beijing.aliyuncs.com/property-img-file/com_communitySearchIcon.png"
|
||
@click="search">
|
||
</image>
|
||
<u--input v-model="searchQuery" @focus="iptFocus" @blur="iptBlur" @confirm="search" placeholder="搜索商品"
|
||
border="surround" clearable></u--input>
|
||
</view>
|
||
<view class="searchBox_right">
|
||
<view class="cars" @click="goCar">
|
||
<u-badge numberType="limit" type="error" max="99" :value="value"></u-badge>
|
||
<image src="https://wechat-img-file.oss-cn-beijing.aliyuncs.com/property-img-file/shop_car.png"
|
||
mode="widthFix"></image>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<!-- 搜索联系 -->
|
||
<!-- <view class="searchMore" v-if="!isSearched && searchHistory.length > 0">
|
||
<view class="searchMoreItem">猪肉肠</view>
|
||
<view class="searchMoreItem">猪肉肠</view>
|
||
<view class="searchMoreItem">猪肉肠</view>
|
||
</view> -->
|
||
|
||
<!-- 搜索历史 -->
|
||
<view class="his" v-if="!isSearched && searchHistory.length > 0">
|
||
<view class="hisTop">
|
||
<view class="hisTit">搜索历史</view>
|
||
<u-icon name="trash" color="#999999" size="40" class="history-trash" @click="deleteHistory"></u-icon>
|
||
</view>
|
||
<view class="HisList">
|
||
<view v-for="(item, index) in searchHistory" :key="index" class="HisItem" @click="selectHistory(item)">
|
||
{{ item }}</view>
|
||
</view>
|
||
</view>
|
||
|
||
|
||
<!-- 未搜索到 -->
|
||
<view class="empty" v-if="isSearched && searchGoodsLisat.length == 0">
|
||
<image src="https://wechat-img-file.oss-cn-beijing.aliyuncs.com/property-img-file/com_noSearch.png"></image>
|
||
对不起没有找到您想要的商品
|
||
</view>
|
||
|
||
<view class="searchList" v-if="isSearched && searchGoodsLisat.length > 0">
|
||
<!-- <view class="searchSubTit">
|
||
<view class="searchSubItem">综合</view>
|
||
<view class="searchSubItem">
|
||
价格
|
||
<view class="iconBox">
|
||
</view>
|
||
</view>
|
||
</view> -->
|
||
|
||
<view v-for="(item, index) in searchGoodsLisat" :key="index">
|
||
<view class="CateInfo_Item_Box">
|
||
<view class="CateInfo_Item_left" @click="goods(item)">
|
||
<view class="tag tag-img" v-if="
|
||
!item.commodity_goods_info_list[1] &&
|
||
item.commodity_goods_info_list[0].is_same_day
|
||
">当日达</view>
|
||
<image :src="handleImageUrl(item.commodity_pic)" mode="aspectFill"></image>
|
||
</view>
|
||
<view class="CateInfo_Item_right" :class="GGshow ? 'noneBor' : ''">
|
||
<view class="CateInfo_Item_right_Tit" @click="goods(item)">
|
||
<view class="tag tag-text" v-if="
|
||
!item.commodity_goods_info_list[1] &&
|
||
item.commodity_goods_info_list[0].is_same_day
|
||
">当日达</view>
|
||
{{ item.commodity_name }}
|
||
</view>
|
||
<view class="CateInfo_Item_right_subtit" @click="goods(item)">
|
||
{{ item.commodity_intro }}
|
||
</view>
|
||
<view class="CateInfo_Item_Money">
|
||
<view class="CateInfo_Item_Money_left">
|
||
{{ getPriceRange(item.commodity_goods_info_list) }}
|
||
</view>
|
||
<view class="CateInfo_Item_Money_right" v-if="!(item.commodity_goods_info_list.length > 1)">
|
||
<u-number-box :value="item.commodity_goods_info_list[0].quantity || 0" :min="0"
|
||
@change="(value) => handleQuantityChange(value, item)">
|
||
<view slot="minus" class="minus">
|
||
<u-icon name="minus" size="20"></u-icon>
|
||
</view>
|
||
<text slot="input" style="width: 50px; text-align: center" class="input">{{
|
||
item.commodity_goods_info_list[0].quantity
|
||
? item.commodity_goods_info_list[0].quantity
|
||
: 0
|
||
}}</text>
|
||
<view slot="plus" class="plus">
|
||
<u-icon name="plus" color="#FFFFFF" size="20"></u-icon>
|
||
</view>
|
||
</u-number-box>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="gg" @click="chooseGG(item)" v-if="
|
||
item.commodity_goods_info_list.length > 1 && !item.isShow
|
||
">
|
||
选择规格
|
||
<u-icon name="arrow-down" size="26rpx" color="#FF370B"></u-icon>
|
||
</view>
|
||
|
||
<view class="gg" @click="chooseGG(item)" v-if="
|
||
item.commodity_goods_info_list.length > 1 && item.isShow
|
||
">
|
||
收起
|
||
<u-icon name="arrow-up" size="26rpx" color="#FF370B"></u-icon>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="GGList" v-if="item.isShow">
|
||
<view class="GGItem" v-for="ite in item.commodity_goods_info_list" :key="ite.id"
|
||
@click="goods(item)">
|
||
<view class="GGItem_Image">
|
||
<view class="tag tag-img" v-if="ite.is_same_day">当日达</view>
|
||
<image :src="handleImageUrl(ite.commodity_pic)" mode="aspectFill"></image>
|
||
</view>
|
||
<view class="GGItem_Con">
|
||
<view class="GGItem_Con_Tit">
|
||
<view class="tag tag-text" v-if="ite.is_same_day">当日达</view>
|
||
{{ ite.goods_name }}
|
||
</view>
|
||
<view class="GGItem_Con_Msg">
|
||
<view class="GGItem_Con_Msg_left">
|
||
<span>¥</span>{{ ite.sales_price }}
|
||
</view>
|
||
<view class="GGItem_Con_Msg_right">
|
||
<u-number-box :value="ite.quantity || 0" :min="0"
|
||
@change="(value) => handleQuantityChange(value, ite)">
|
||
<view slot="minus" class="minus">
|
||
<u-icon name="minus" size="20"></u-icon>
|
||
</view>
|
||
<text slot="input" style="width: 50px; text-align: center" class="input">{{
|
||
ite.quantity ?
|
||
ite.quantity : 0 }}</text>
|
||
<view slot="plus" class="plus">
|
||
<u-icon name="plus" color="#FFFFFF" size="20"></u-icon>
|
||
</view>
|
||
</u-number-box>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import {
|
||
apiArr
|
||
} from '../../../api/shop';
|
||
import {
|
||
picUrl as basePicUrl,
|
||
menuButtonInfo,
|
||
request,
|
||
NavgateTo
|
||
} from '../../../utils';
|
||
export default {
|
||
data() {
|
||
return {
|
||
flag: false,
|
||
value: 0,
|
||
isSearch: false,
|
||
isSearched: false,
|
||
searchQuery: '',
|
||
searchHistory: [],
|
||
searchGoodsLisat: [],
|
||
goodsDetail: []
|
||
}
|
||
},
|
||
methods: {
|
||
goCar() {
|
||
NavgateTo('/packages/shop/shopCar/index')
|
||
},
|
||
getShopCar() {
|
||
request(apiArr.getCar, "POST", {}).then((res) => {
|
||
this.value = res.total;
|
||
});
|
||
},
|
||
// 获取购物车数据
|
||
getShopCarList() {
|
||
request(apiArr.getCar, "POST").then((res) => {
|
||
this.value = res.total;
|
||
// 合并当日达和普通商品数据
|
||
this.goodsDetail = [].concat(res.same_day_cart_list || [], res.normal_cart_list || [])
|
||
.flatMap(supplier => supplier.commodity_cart_and_goods_model || []);
|
||
});
|
||
},
|
||
search() {
|
||
if (this.searchQuery.trim()) {
|
||
// 保存搜索历史到storage
|
||
this.saveSearchHistory();
|
||
// 设置搜索状态为已搜索
|
||
this.isSearched = true;
|
||
// 隐藏搜索历史面板
|
||
this.isSearch = false;
|
||
const params = {
|
||
query: this.searchQuery,
|
||
user_id: uni.getStorageSync('userId')
|
||
}
|
||
request(apiArr.goodsSearch, "POST", params).then((res) => {
|
||
// 深拷贝接口数据,避免引用问题
|
||
const commodityList = JSON.parse(JSON.stringify(res.commodity_list));
|
||
commodityList.forEach((item) => {
|
||
// 初始化isShow为响应式属性
|
||
this.$set(item, 'isShow', false);
|
||
item.commodity_goods_info_list.forEach((param) => {
|
||
// 从购物车数据中查找对应商品的数量,否则设为0
|
||
const goods = this.goodsDetail.find(g => g.goods_id === param.id);
|
||
this.$set(param, 'quantity', goods ? goods.count : 0);
|
||
});
|
||
});
|
||
this.searchGoodsLisat = commodityList;
|
||
});
|
||
}
|
||
},
|
||
ipt1(e) {
|
||
console.log(e);
|
||
},
|
||
iptFocus() {
|
||
this.isSearch = true;
|
||
this.loadSearchHistory();
|
||
},
|
||
iptBlur() {
|
||
if (this.searchQuery) {
|
||
this.search();
|
||
} else {
|
||
this.searchGoodsLisat = [];
|
||
this.isSearch = true
|
||
this.isSearched = false
|
||
}
|
||
},
|
||
// 保存搜索历史
|
||
saveSearchHistory() {
|
||
if (this.searchQuery && !this.searchHistory.includes(this.searchQuery)) {
|
||
// 添加到数组开头
|
||
this.searchHistory.unshift(this.searchQuery);
|
||
// 限制最多保存10条历史记录
|
||
if (this.searchHistory.length > 10) {
|
||
this.searchHistory.pop();
|
||
}
|
||
// 保存到storage
|
||
uni.setStorageSync('shopSearchHistory', this.searchHistory);
|
||
}
|
||
},
|
||
// 加载搜索历史
|
||
loadSearchHistory() {
|
||
const history = uni.getStorageSync('shopSearchHistory') || [];
|
||
this.searchHistory = history;
|
||
},
|
||
// 选择历史记录进行搜索
|
||
selectHistory(item) {
|
||
this.searchQuery = item;
|
||
this.search();
|
||
this.isSearch = false;
|
||
},
|
||
// 清除搜索历史
|
||
deleteHistory() {
|
||
uni.showModal({
|
||
title: '提示',
|
||
content: '确定清除所有搜索历史吗?',
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
this.searchHistory = [];
|
||
uni.removeStorageSync('shopSearchHistory');
|
||
uni.showToast({
|
||
title: '搜索历史已清除',
|
||
icon: 'none'
|
||
});
|
||
}
|
||
}
|
||
});
|
||
},
|
||
// 商品详情页
|
||
goods(e) {
|
||
NavgateTo(`../goods/index?item=${JSON.stringify(e)}`);
|
||
},
|
||
// 选择商品规格
|
||
chooseGG(targetItems) {
|
||
// 遍历商品列表找到目标对象
|
||
const itemIndex = this.searchGoodsLisat.findIndex(item => item.id === targetItems.id);
|
||
if (itemIndex > -1) {
|
||
// 直接修改数据源中的对象,确保响应式
|
||
const currentItem = this.searchGoodsLisat[itemIndex];
|
||
this.$set(currentItem, 'isShow', !currentItem.isShow);
|
||
}
|
||
},
|
||
// 处理商品数量变化
|
||
handleQuantityChange(val, item) {
|
||
const quantity = typeof val === 'object' && val !== null && 'value' in val ? val.value : val;
|
||
|
||
// 检查库存数量
|
||
let currentQuantity = 0;
|
||
let stockQuantity = 0;
|
||
let goodsToUpdate = null;
|
||
|
||
// 对于有规格的主商品(绑定到items.commodity_goods_info_list[0].quantity)
|
||
if (
|
||
item.commodity_goods_info_list &&
|
||
item.commodity_goods_info_list.length
|
||
) {
|
||
this.goodsId = item.commodity_goods_info_list[0].id;
|
||
currentQuantity = item.commodity_goods_info_list[0].quantity || 0;
|
||
stockQuantity = item.commodity_goods_info_list[0].stock_quantity || 0;
|
||
goodsToUpdate = item.commodity_goods_info_list[0];
|
||
}
|
||
// 对于规格列表中的商品(绑定到ite.quantity)
|
||
else {
|
||
this.goodsId = item.id;
|
||
currentQuantity = item.quantity || 0;
|
||
stockQuantity = item.stock_quantity || 0;
|
||
goodsToUpdate = item;
|
||
}
|
||
|
||
// 直接检查新数量是否超过库存,如果超过则不允许修改
|
||
if (quantity > stockQuantity) {
|
||
uni.showToast({
|
||
title: "库存不足",
|
||
icon: 'none'
|
||
});
|
||
// 强制重置数量为当前值或库存值,确保UI上显示的数量不会超过库存
|
||
this.$nextTick(() => {
|
||
this.$set(goodsToUpdate, 'quantity', Math.min(currentQuantity, stockQuantity));
|
||
});
|
||
return;
|
||
}
|
||
|
||
// 使用$set确保响应式更新
|
||
this.$set(goodsToUpdate, 'quantity', quantity);
|
||
|
||
const params = {
|
||
user_id: uni.getStorageSync("userId"),
|
||
goods_id_and_count: [
|
||
{
|
||
goods_id: this.goodsId,
|
||
count: quantity,
|
||
},
|
||
],
|
||
};
|
||
|
||
// 发送请求更新后端数据
|
||
request(apiArr.updateCar, "POST", params).then((res) => {
|
||
console.log(res);
|
||
// 先更新购物车数据
|
||
this.getShopCarList();
|
||
|
||
// 延迟一小段时间,确保goodsDetail已经更新
|
||
setTimeout(() => {
|
||
// 重新同步商品列表中的数量
|
||
this.syncGoodsQuantities();
|
||
}, 100);
|
||
|
||
uni.showToast({
|
||
title: "操作成功!",
|
||
success() { },
|
||
});
|
||
});
|
||
},
|
||
|
||
// 同步商品列表中的数量与购物车数据
|
||
syncGoodsQuantities() {
|
||
// 遍历所有商品,同步数量
|
||
this.searchGoodsLisat.forEach((item) => {
|
||
item.commodity_goods_info_list.forEach((param) => {
|
||
const goods = this.goodsDetail.find(g => g.goods_id === param.id);
|
||
if (goods) {
|
||
this.$set(param, 'quantity', goods.count);
|
||
}
|
||
});
|
||
});
|
||
},
|
||
// 获取价格范围
|
||
getPriceRange(goodsList) {
|
||
if (!goodsList || goodsList.length === 0) return '¥0';
|
||
const prices = goodsList.map(item => Number(item.sales_price));
|
||
const minPrice = Math.min(...prices);
|
||
const maxPrice = Math.max(...prices);
|
||
return minPrice === maxPrice ? `¥${minPrice}` : `¥${minPrice} ~ ¥${maxPrice}`;
|
||
},
|
||
|
||
// 处理图片路径
|
||
handleImageUrl(imagePath) {
|
||
return basePicUrl + imagePath;
|
||
}
|
||
},
|
||
watch: {
|
||
searchQuery: {
|
||
handler(newVal, oldVal) {
|
||
if (!newVal) {
|
||
this.searchGoodsLisat = [];
|
||
this.isSearch = true
|
||
this.isSearched = false
|
||
}
|
||
},
|
||
deep: true
|
||
}
|
||
},
|
||
|
||
onLoad(options) {
|
||
this.loadSearchHistory();
|
||
this.isSearch = false;
|
||
this.isSearched = false;
|
||
},
|
||
onShow() {
|
||
this.getShopCarList();
|
||
},
|
||
onReachBottom() {
|
||
if (this.flag) {
|
||
|
||
}
|
||
},
|
||
}
|
||
</script>
|
||
|
||
<style>
|
||
@import url("./index.css");
|
||
</style> |