Files
medical-mall/pages/main/cart-search/cart-search.uvue
2026-05-14 15:28:09 +08:00

1307 lines
28 KiB
Plaintext
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="cart-search-page">
<view class="search-header" :style="{ paddingTop: statusBarHeight + 'px', paddingRight: searchHeaderRightPadding + 'px' }">
<view class="back-btn" @click="goBack">
<text class="back-icon"></text>
</view>
<view class="search-input-wrap">
<text class="search-icon">⌕</text>
<input
class="search-input"
v-model="keyword"
placeholder="搜索购物车商品"
confirm-type="search"
:focus="true"
@confirm="doSearch"
/>
<view v-if="keyword.length > 0" class="clear-keyword" @click="clearKeyword">
<text class="clear-keyword-text">×</text>
</view>
</view>
<view class="search-btn" @click="doSearch">
<text class="search-btn-text">搜索</text>
</view>
</view>
<scroll-view v-if="!hasSearched" class="search-content" :scroll-y="true" :show-scrollbar="false">
<view class="history-section">
<view class="section-header">
<text class="section-title">历史搜索</text>
<text class="clear-history" @click="clearSearchHistory">清空</text>
</view>
<view v-if="searchHistory.length > 0" class="history-list">
<view
v-for="item in searchHistory"
:key="item"
class="history-chip"
@click="useSearchWord(item)"
>
<text class="history-chip-text">{{ item }}</text>
</view>
</view>
<view v-else class="empty-history">
<text class="empty-history-text">暂无历史搜索</text>
</view>
</view>
<view class="discover-section">
<view class="section-header">
<text class="section-title">搜索发现</text>
</view>
<view class="discover-grid">
<view
v-for="item in searchDiscoverWords"
:key="item"
class="discover-item"
@click="useSearchWord(item)"
>
<text class="discover-text">{{ item }}</text>
</view>
</view>
</view>
</scroll-view>
<scroll-view v-else class="search-result-content" :scroll-y="true" :show-scrollbar="false">
<view v-if="matchedCartItems.length > 0" class="cart-match-section">
<view class="result-section-title-wrap">
<text class="result-section-title">购物车内相关商品</text>
</view>
<view class="cart-result-list">
<view
v-for="item in matchedCartItems"
:key="item.id"
class="cart-result-card"
>
<view class="item-select" @click="toggleSelect(item.id)">
<text v-if="item.selected" class="selected-icon">✓</text>
<text v-else class="unselected-icon"></text>
</view>
<image class="item-image" :src="item.image" mode="aspectFill" @click="goToProductDetailFromCart(item)" />
<view class="result-item-info">
<text class="result-shop-name">{{ item.shopName }}</text>
<text class="item-name" :lines="1">{{ item.name }}</text>
<text class="item-spec">{{ item.spec }}</text>
<view class="item-footer">
<text class="item-price">¥{{ item.price }}</text>
<view class="quantity-control">
<text class="quantity-btn" @click="decreaseQuantity(item.id)">-</text>
<text class="quantity-value">{{ item.quantity }}</text>
<text class="quantity-btn" @click="increaseQuantity(item.id)">+</text>
</view>
</view>
</view>
</view>
</view>
</view>
<view v-else class="no-cart-result">
<view class="warning-row">
<text class="warning-icon">!</text>
<text class="warning-text">{{ noCartResultText }}</text>
</view>
</view>
<view class="recommend-search-section">
<view class="recommend-title-wrap">
<view class="line"></view>
<text class="recommend-title">{{ matchedCartItems.length > 0 ? '为你搜索全站商品' : '为你搜索全部商品' }}</text>
<view class="line"></view>
</view>
<view class="recommend-grid">
<view
v-for="product in recommendProducts"
:key="product.id"
class="recommend-card"
@click="goToProductDetail(product)"
>
<image class="recommend-image" :src="product.image" mode="aspectFill" />
<view class="recommend-info">
<text class="recommend-shop-tag">{{ product.shopName }}</text>
<text class="recommend-name" :lines="2">{{ product.name }}</text>
<text class="recommend-sales">{{ product.salesText }}</text>
<view class="recommend-price-row">
<text class="recommend-price">¥{{ product.price }}</text>
<view class="recommend-cart-btn" @click.stop="addRecommendToCart(product)">
<text class="recommend-cart-icon">🛒</text>
</view>
</view>
</view>
</view>
</view>
<view class="bottom-safe-space"></view>
</view>
</scroll-view>
<view v-if="hasSearched" class="fixed-cart-settlement-bar">
<view class="settlement-inner">
<view class="settlement-left" @click="toggleSelectAllInSearch">
<view class="select-circle" :class="{ 'select-circle-active': allSearchSelected }">
<text v-if="allSearchSelected" class="select-check">✓</text>
</view>
<text class="select-all-text">全选</text>
</view>
<view class="settlement-right">
<view class="total-info">
<text class="total-label">合计:</text>
<text class="total-price">¥{{ searchTotalPrice }}</text>
</view>
<button class="checkout-btn" :class="{ 'checkout-btn-disabled': searchSelectedCount == 0 }" @click="goToCheckoutFromSearch">
去结算({{ searchSelectedCount }})
</button>
</view>
</view>
</view>
</view>
</template>
<script setup lang="uts">
import { computed, ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { supabaseService, type CartItem as SupabaseCartItem, type Product } from '@/utils/supabaseService.uts'
import { goToLogin } from '@/utils/utils.uts'
const CART_SEARCH_HISTORY_KEY = 'cart_search_history'
type LocalCartItem = {
id: string
shopId: string
shopName: string
name: string
productName: string
skuName: string
specName: string
merchantName: string
brandName: string
price: number
originalPrice: number
memberPrice: number
image: string
spec: string
quantity: number
selected: boolean
productId: string
skuId: string
merchantId: string
}
type ProductItem = {
id: string
name: string
price: number
image: string
shopName: string
salesText: string
merchantId: string
skuId: string
}
const keyword = ref<string>('')
const hasSearched = ref<boolean>(false)
const searchHistory = ref<Array<string>>([])
const searchDiscoverList = ref<Array<string>>([
'无人机',
'水杯',
'手机',
'天然泉水',
'按摩仪',
'摄像头',
'耳机',
'停车场设备',
'饮料',
'iPhone'
])
const cartItems = ref<Array<LocalCartItem>>([])
const recommendProducts = ref<Array<ProductItem>>([])
const isLoading = ref<boolean>(false)
const statusBarHeight = ref<number>(0)
const updatingItems = ref<Set<string>>(new Set())
const navBarRight = ref<number>(12)
const searchDiscoverWords = computed<Array<string>>(() => {
if (searchDiscoverList.value.length > 0) {
return searchDiscoverList.value
}
return ['无人机', '水杯', '手机', '天然泉水']
})
const safeLower = (value: string): string => {
return value.toLowerCase()
}
const matchedCartItems = computed<Array<LocalCartItem>>(() => {
const q = keyword.value.trim().toLowerCase()
if (q == '') {
return []
}
return cartItems.value.filter((item: LocalCartItem) => {
const title = safeLower(item.name)
const name = safeLower(item.name)
const productName = safeLower(item.productName)
const skuName = safeLower(item.skuName)
const specName = safeLower(item.specName)
const shopName = safeLower(item.shopName)
const merchantName = safeLower(item.merchantName)
const brandName = safeLower(item.brandName)
return title.indexOf(q) >= 0
|| name.indexOf(q) >= 0
|| productName.indexOf(q) >= 0
|| skuName.indexOf(q) >= 0
|| specName.indexOf(q) >= 0
|| shopName.indexOf(q) >= 0
|| merchantName.indexOf(q) >= 0
|| brandName.indexOf(q) >= 0
})
})
const searchSelectedItems = computed<Array<LocalCartItem>>(() => {
return matchedCartItems.value.filter((item: LocalCartItem) => item.selected == true)
})
const searchSelectedCount = computed((): number => {
return searchSelectedItems.value.length
})
const searchTotalPrice = computed((): string => {
let total = 0
searchSelectedItems.value.forEach((item: LocalCartItem) => {
const finalPrice = item.memberPrice > 0 && item.memberPrice < item.price ? item.memberPrice : item.price
total += finalPrice * item.quantity
})
return total.toFixed(2)
})
const allSearchSelected = computed((): boolean => {
if (matchedCartItems.value.length == 0) return false
return matchedCartItems.value.every((item: LocalCartItem) => item.selected == true)
})
const noCartResultText = computed((): string => {
if (cartItems.value.length == 0) {
return '购物车为空,暂无相关商品'
}
return '您的购物车里没有相关商品'
})
const searchHeaderRightPadding = computed((): number => {
if (navBarRight.value > 12) {
return navBarRight.value
}
return 12
})
const getItemById = (itemId: string): LocalCartItem | null => {
const index = cartItems.value.findIndex((item: LocalCartItem) => item.id == itemId)
if (index == -1) return null
return cartItems.value[index]
}
const loadSearchHistory = () => {
const cache = uni.getStorageSync(CART_SEARCH_HISTORY_KEY)
if (cache == null || cache == '') {
searchHistory.value = []
return
}
if (Array.isArray(cache)) {
searchHistory.value = cache as Array<string>
return
}
if (typeof cache == 'string') {
try {
const parsed = JSON.parse(cache) as Array<string>
searchHistory.value = parsed
} catch (e) {
console.error('解析搜索历史失败:', e)
searchHistory.value = []
}
}
}
const saveSearchHistory = (word: string) => {
const text = word.trim()
if (text == '') return
let list = searchHistory.value.filter((item: string) => item != text)
list.unshift(text)
if (list.length > 10) {
list = list.slice(0, 10)
}
searchHistory.value = list
uni.setStorageSync(CART_SEARCH_HISTORY_KEY, JSON.stringify(list))
}
const clearSearchHistory = () => {
searchHistory.value = []
uni.removeStorageSync(CART_SEARCH_HISTORY_KEY)
}
const mockRecommendProducts = (q: string): Array<ProductItem> => {
const text = q.trim() == '' ? '热卖好物' : q
return [
{
id: 'mock-1',
name: text + ' 便携款',
price: 99,
image: '/static/images/default.png',
shopName: '平台精选',
salesText: '已售 200+',
merchantId: '',
skuId: ''
},
{
id: 'mock-2',
name: text + ' 升级版',
price: 159,
image: '/static/images/default.png',
shopName: '品牌旗舰',
salesText: '好评 98%',
merchantId: '',
skuId: ''
},
{
id: 'mock-3',
name: text + ' 热销套装',
price: 239,
image: '/static/images/default.png',
shopName: '今日推荐',
salesText: '月销 500+',
merchantId: '',
skuId: ''
},
{
id: 'mock-4',
name: text + ' 家用精选',
price: 79,
image: '/static/images/default.png',
shopName: '官方自营',
salesText: '已售 1200+',
merchantId: '',
skuId: ''
}
]
}
const loadRecommendProducts = async (q: string) => {
isLoading.value = true
try {
const result = await supabaseService.searchProducts(q, 1, 8, 'sales')
if (result.data.length > 0) {
recommendProducts.value = result.data.map((product: Product): ProductItem => {
const saleCount = product.sale_count ?? product.sales ?? 0
return {
id: product.id,
name: product.name,
price: product.base_price ?? product.market_price ?? 0,
image: product.main_image_url ?? product.image_url ?? '/static/images/default.png',
shopName: product.shop_name ?? '平台精选',
salesText: '已售 ' + saleCount + '+',
merchantId: product.merchant_id ?? '',
skuId: ''
}
})
} else {
recommendProducts.value = mockRecommendProducts(q)
}
} catch (e) {
console.error('加载推荐商品失败:', e)
recommendProducts.value = mockRecommendProducts(q)
} finally {
isLoading.value = false
}
}
const loadCartData = async () => {
isLoading.value = true
try {
let memberDiscount = 1.0
try {
const memberInfo = await supabaseService.getUserMemberInfo()
const discountRaw = memberInfo.get('discount')
if (discountRaw != null) {
memberDiscount = discountRaw as number
}
} catch (e) {
console.log('获取会员信息失败,使用默认折扣:', e)
}
const supabaseCartItems = await supabaseService.getCartItems()
cartItems.value = supabaseCartItems.map((item: SupabaseCartItem): LocalCartItem => {
const originalPrice = item.product_price ?? 0
let memberPrice = 0
if (memberDiscount > 0 && memberDiscount < 1 && originalPrice > 0) {
memberPrice = Math.round(originalPrice * memberDiscount * 100) / 100
}
const productName = item.product_name ?? '未知商品'
const specName = item.product_specification ?? '标准规格'
return {
id: item.id,
shopId: item.shop_id ?? 'default_shop',
shopName: item.shop_name ?? '商城优选',
name: productName,
productName: productName,
skuName: specName,
specName: specName,
merchantName: item.shop_name ?? '商城优选',
brandName: '',
price: originalPrice,
originalPrice: originalPrice,
memberPrice: memberPrice,
image: item.product_image ?? '/static/images/default.png',
spec: specName,
quantity: item.quantity ?? 1,
selected: item.selected ?? false,
productId: item.product_id ?? '',
skuId: item.sku_id ?? '',
merchantId: item.merchant_id ?? ''
}
})
} catch (e) {
console.error('加载购物车搜索数据失败:', e)
cartItems.value = []
} finally {
isLoading.value = false
}
}
const doSearch = async () => {
const q = keyword.value.trim()
if (q == '') {
uni.showToast({
title: '请输入搜索关键词',
icon: 'none'
})
return
}
hasSearched.value = true
saveSearchHistory(q)
await loadRecommendProducts(q)
}
const useSearchWord = (word: string) => {
keyword.value = word
doSearch()
}
const goBack = () => {
uni.navigateBack()
}
const clearKeyword = () => {
keyword.value = ''
hasSearched.value = false
recommendProducts.value = []
}
const toggleSelect = async (itemId: string) => {
const index = cartItems.value.findIndex((item: LocalCartItem) => item.id == itemId)
if (index == -1) return
const newSelected = !cartItems.value[index].selected
cartItems.value[index].selected = newSelected
cartItems.value = [...cartItems.value]
const success = await supabaseService.updateCartItemSelection(itemId, newSelected)
if (!success) {
cartItems.value[index].selected = !newSelected
cartItems.value = [...cartItems.value]
uni.showToast({ title: '网络异常,请重试', icon: 'none' })
}
}
const increaseQuantity = async (itemId: string) => {
if (updatingItems.value.has(itemId)) return
const index = cartItems.value.findIndex((item: LocalCartItem) => item.id == itemId)
if (index == -1) return
updatingItems.value.add(itemId)
const newQuantity = cartItems.value[index].quantity + 1
cartItems.value[index].quantity = newQuantity
cartItems.value = [...cartItems.value]
const success = await supabaseService.updateCartItemQuantity(itemId, newQuantity)
updatingItems.value.delete(itemId)
if (!success) {
cartItems.value[index].quantity = newQuantity - 1
cartItems.value = [...cartItems.value]
uni.showToast({ title: '更新失败', icon: 'none' })
}
}
const decreaseQuantity = async (itemId: string) => {
if (updatingItems.value.has(itemId)) return
const index = cartItems.value.findIndex((item: LocalCartItem) => item.id == itemId)
if (index == -1) return
if (cartItems.value[index].quantity <= 1) {
uni.showToast({ title: '最少保留1件可返回购物车删除', icon: 'none' })
return
}
updatingItems.value.add(itemId)
const newQuantity = cartItems.value[index].quantity - 1
cartItems.value[index].quantity = newQuantity
cartItems.value = [...cartItems.value]
const success = await supabaseService.updateCartItemQuantity(itemId, newQuantity)
updatingItems.value.delete(itemId)
if (!success) {
cartItems.value[index].quantity = newQuantity + 1
cartItems.value = [...cartItems.value]
uni.showToast({ title: '更新失败', icon: 'none' })
}
}
const toggleSelectAllInSearch = async () => {
const checked = !allSearchSelected.value
const ids: Array<string> = []
matchedCartItems.value.forEach((item: LocalCartItem) => {
item.selected = checked
ids.push(item.id)
})
cartItems.value = [...cartItems.value]
if (ids.length == 0) return
const success = await supabaseService.batchUpdateCartItemSelection(ids, checked)
if (!success) {
matchedCartItems.value.forEach((item: LocalCartItem) => {
item.selected = !checked
})
cartItems.value = [...cartItems.value]
uni.showToast({ title: '操作失败', icon: 'none' })
}
}
const goToCheckoutFromSearch = () => {
if (searchSelectedCount.value == 0) {
uni.showToast({
title: '请选择商品',
icon: 'none'
})
return
}
const selectedItems = searchSelectedItems.value.map((item: LocalCartItem) => {
return {
id: item.id,
product_id: item.productId,
sku_id: item.skuId,
product_name: item.name,
shop_id: item.shopId,
shop_name: item.shopName,
merchant_id: item.merchantId,
product_image: item.image,
sku_specifications: item.spec,
price: item.price,
quantity: item.quantity
}
})
uni.setStorageSync('checkout_type', 'cart')
try {
uni.setStorageSync('checkout_items', JSON.stringify(selectedItems))
} catch (e) {
console.error('存储结算数据失败', e)
uni.showToast({ title: '系统异常,请重试', icon: 'none' })
return
}
uni.navigateTo({
url: '/pages/mall/consumer/checkout'
})
}
const goToProductDetailFromCart = (item: LocalCartItem) => {
const productId = item.productId == '' ? item.id : item.productId
let paramsArr: Array<string> = []
paramsArr.push('id=' + encodeURIComponent(productId))
paramsArr.push('productId=' + encodeURIComponent(productId))
paramsArr.push('price=' + item.price)
paramsArr.push('originalPrice=' + item.originalPrice)
paramsArr.push('name=' + encodeURIComponent(item.name))
paramsArr.push('image=' + encodeURIComponent(item.image))
uni.navigateTo({
url: '/pages/mall/consumer/product-detail?' + paramsArr.join('&')
})
}
const goToProductDetail = (product: ProductItem) => {
let url = '/pages/mall/consumer/product-detail?id=' + encodeURIComponent(product.id)
url += '&productId=' + encodeURIComponent(product.id)
url += '&price=' + product.price
url += '&name=' + encodeURIComponent(product.name)
url += '&image=' + encodeURIComponent(product.image)
uni.navigateTo({ url })
}
const addRecommendToCart = async (product: ProductItem) => {
const userId = supabaseService.getCurrentUserId()
if (userId == null || userId == '') {
goToLogin('/pages/main/cart-search/cart-search')
return
}
uni.showLoading({ title: '添加中...' })
try {
const skus = await supabaseService.getProductSkus(product.id)
if (skus.length > 0) {
uni.hideLoading()
uni.showToast({ title: '请选择规格', icon: 'none' })
setTimeout(() => {
goToProductDetail(product)
}, 400)
return
}
const success = await supabaseService.addToCart(product.id, 1, product.skuId, product.merchantId)
uni.hideLoading()
if (success) {
uni.showToast({ title: '已添加到购物车', icon: 'success' })
loadCartData()
} else {
uni.showToast({ title: '添加失败', icon: 'none' })
}
} catch (e) {
console.error('推荐商品加入购物车失败:', e)
uni.hideLoading()
uni.showToast({ title: '添加失败', icon: 'none' })
}
}
onLoad((options: UTSJSONObject) => {
const systemInfo = uni.getSystemInfoSync()
statusBarHeight.value = systemInfo.statusBarHeight ?? 0
// #ifdef MP-WEIXIN
try {
const menuButton = uni.getMenuButtonBoundingClientRect()
if (menuButton != null) {
navBarRight.value = (systemInfo.screenWidth - menuButton.left) + 10
}
} catch (e) {
console.log('获取胶囊按钮信息失败:', e)
navBarRight.value = 96
}
// #endif
loadSearchHistory()
loadCartData()
const word = options.getString('keyword')
if (word != null && word != '') {
const decodedWord = decodeURIComponent(word)
keyword.value = decodedWord != null ? decodedWord : word
doSearch()
}
})
</script>
<style>
.cart-search-page {
width: 100%;
height: 100%;
background-color: #f5f5f5;
display: flex;
flex-direction: column;
overflow: hidden;
}
.search-header {
height: 56px;
padding: 0 12px;
background-color: #ffffff;
display: flex;
flex-direction: row;
align-items: center;
box-sizing: content-box;
border-bottom: 1px solid #f1f1f1;
}
.back-btn {
width: 36px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
}
.back-icon {
font-size: 34px;
color: #222222;
line-height: 34px;
}
.search-input-wrap {
flex: 1;
height: 38px;
border-radius: 20px;
background-color: #f5f5f5;
display: flex;
flex-direction: row;
align-items: center;
padding: 0 12px;
min-width: 0;
}
.search-icon {
font-size: 15px;
color: #999999;
margin-right: 6px;
}
.search-input {
flex: 1;
height: 38px;
font-size: 15px;
color: #222222;
background-color: transparent;
border: none;
padding: 0;
margin: 0;
}
.clear-keyword {
width: 24px;
height: 24px;
border-radius: 12px;
background-color: #dddddd;
display: flex;
align-items: center;
justify-content: center;
}
.clear-keyword-text {
font-size: 16px;
color: #666666;
line-height: 16px;
}
.search-btn {
height: 36px;
padding: 0 14px;
margin-left: 8px;
border-radius: 18px;
background-color: #ff5000;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.search-btn-text {
color: #ffffff;
font-size: 14px;
font-weight: 600;
white-space: nowrap;
}
.search-content,
.search-result-content {
flex: 1;
height: 0;
background-color: #f5f5f5;
}
.history-section,
.discover-section {
padding: 18px 16px 0 16px;
background-color: #ffffff;
}
.discover-section {
margin-top: 12px;
padding-bottom: 18px;
}
.section-header {
height: 32px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
}
.section-title {
font-size: 16px;
color: #222222;
font-weight: 700;
}
.clear-history {
font-size: 13px;
color: #999999;
}
.history-list {
display: flex;
flex-direction: row;
flex-wrap: wrap;
padding-top: 8px;
}
.history-chip {
height: 30px;
padding: 0 14px;
margin-right: 10px;
margin-bottom: 10px;
border-radius: 15px;
background-color: #f5f5f5;
display: flex;
align-items: center;
justify-content: center;
}
.history-chip-text {
font-size: 14px;
color: #333333;
}
.empty-history {
padding: 20px 0 12px 0;
display: flex;
justify-content: center;
}
.empty-history-text {
font-size: 14px;
color: #999999;
}
.discover-grid {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-between;
padding-top: 8px;
}
.discover-item {
width: 48%;
height: 44px;
margin-bottom: 8px;
border-radius: 6px;
background-color: #f7f7f7;
display: flex;
align-items: center;
justify-content: center;
}
.discover-text {
font-size: 14px;
color: #333333;
}
.cart-match-section {
padding-top: 12px;
background-color: #f5f5f5;
}
.result-section-title-wrap {
padding: 0 16px 10px 16px;
}
.result-section-title {
font-size: 16px;
color: #222222;
font-weight: 700;
}
.cart-result-list {
padding: 0 12px;
}
.cart-result-card {
background-color: #ffffff;
border-radius: 12px;
padding: 12px;
margin-bottom: 12px;
display: flex;
flex-direction: row;
align-items: center;
}
.result-item-info {
flex: 1;
display: flex;
flex-direction: column;
height: 76px;
justify-content: space-between;
overflow: hidden;
}
.result-shop-name {
font-size: 12px;
color: #999999;
margin-bottom: 2px;
}
.item-select {
width: 30px;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 6px;
flex-shrink: 0;
}
.selected-icon {
width: 18px;
height: 18px;
background-color: #ff5000;
color: #ffffff;
border-radius: 9px;
text-align: center;
line-height: 18px;
font-size: 12px;
}
.unselected-icon {
width: 18px;
height: 18px;
border: 1px solid #dddddd;
border-radius: 9px;
}
.item-image {
width: 76px;
height: 76px;
border-radius: 8px;
margin-right: 10px;
flex-shrink: 0;
}
.item-name {
font-size: 14px;
color: #222222;
font-weight: 700;
overflow: hidden;
text-overflow: ellipsis;
}
.item-spec {
font-size: 12px;
color: #999999;
}
.item-footer {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
width: 100%;
}
.item-price {
font-size: 16px;
color: #ff5000;
font-weight: 700;
}
.quantity-control {
display: flex;
flex-direction: row;
align-items: center;
background-color: #f5f5f5;
border-radius: 12px;
overflow: hidden;
height: 28px;
}
.quantity-btn {
width: 28px;
height: 28px;
text-align: center;
line-height: 28px;
font-size: 16px;
color: #333333;
background-color: #eeeeee;
}
.quantity-value {
min-width: 36px;
text-align: center;
font-size: 14px;
line-height: 28px;
color: #333333;
}
.no-cart-result {
padding: 54px 16px 28px 16px;
background-color: #ffffff;
display: flex;
align-items: center;
justify-content: center;
}
.warning-row {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
}
.warning-icon {
width: 22px;
height: 22px;
border-radius: 11px;
background-color: #ff7a00;
color: #ffffff;
font-size: 16px;
text-align: center;
line-height: 22px;
margin-right: 8px;
}
.warning-text {
font-size: 17px;
color: #666666;
font-weight: 500;
}
.recommend-search-section {
padding: 18px 12px 0 12px;
}
.recommend-title-wrap {
height: 36px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
}
.line {
width: 70px;
height: 1px;
background-color: #dddddd;
}
.recommend-title {
font-size: 15px;
color: #999999;
margin: 0 12px;
}
.recommend-grid {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-between;
}
.recommend-card {
width: 49%;
margin-bottom: 12px;
border-radius: 12px;
background-color: #ffffff;
overflow: hidden;
}
.recommend-image {
width: 100%;
height: 180px;
background-color: #eeeeee;
}
.recommend-info {
padding: 8px;
}
.recommend-shop-tag {
font-size: 11px;
color: #ff5000;
margin-bottom: 4px;
}
.recommend-name {
font-size: 15px;
color: #222222;
line-height: 21px;
height: 42px;
overflow: hidden;
}
.recommend-sales {
font-size: 12px;
color: #999999;
margin-top: 6px;
}
.recommend-price-row {
margin-top: 8px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
}
.recommend-price {
font-size: 20px;
color: #ff5000;
font-weight: 700;
}
.recommend-cart-btn {
width: 32px;
height: 32px;
border-radius: 16px;
background-color: #fff2e8;
display: flex;
align-items: center;
justify-content: center;
}
.recommend-cart-icon {
font-size: 16px;
line-height: 16px;
}
.fixed-cart-settlement-bar {
position: fixed;
left: 0;
right: 0;
bottom: 0;
background-color: #ffffff;
border-top: 1px solid #eeeeee;
z-index: 999;
padding-bottom: var(--window-bottom);
}
.settlement-inner {
height: 64px;
padding: 0 14px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
}
.settlement-left {
display: flex;
flex-direction: row;
align-items: center;
flex-shrink: 0;
}
.select-circle {
width: 20px;
height: 20px;
border-radius: 10px;
border: 1px solid #dddddd;
background-color: #ffffff;
display: flex;
align-items: center;
justify-content: center;
}
.select-circle-active {
background-color: #ff5000;
border-color: #ff5000;
}
.select-check {
font-size: 12px;
color: #ffffff;
}
.select-all-text {
font-size: 14px;
color: #333333;
margin-left: 8px;
}
.settlement-right {
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-end;
flex: 1;
min-width: 0;
}
.total-info {
display: flex;
flex-direction: row;
align-items: baseline;
margin-right: 12px;
}
.total-label {
font-size: 14px;
color: #666666;
}
.total-price {
font-size: 20px;
color: #ff5000;
font-weight: 700;
margin-left: 3px;
}
.checkout-btn {
height: 44px;
min-width: 126px;
padding: 0 20px;
border-radius: 24px;
background-color: #ff5000;
color: #ffffff;
font-size: 16px;
font-weight: 700;
line-height: 44px;
border: none;
margin: 0;
}
.checkout-btn-disabled {
background-color: #ffb6a0;
}
.bottom-safe-space {
height: 92px;
}
@media screen and (max-width: 375px) {
.search-header {
padding-left: 10px;
}
.search-btn {
padding: 0 10px;
}
.recommend-image {
height: 160px;
}
.recommend-price,
.total-price {
font-size: 18px;
}
.checkout-btn {
min-width: 110px;
padding: 0 14px;
font-size: 14px;
}
}
</style>