20260227-1

This commit is contained in:
cyh666666
2026-02-27 16:51:56 +08:00
1526 changed files with 2457 additions and 38509 deletions

View File

@@ -119,15 +119,12 @@
class="guess-item"
@click="viewProductDetail(item)"
>
<view class="guess-img-box">
<image class="guess-img" :src="item.image" mode="aspectFill" />
</view>
<view class="guess-info">
<text class="guess-name">{{ item.name }}</text>
<view class="guess-price-row">
<text class="price-symbol">¥</text>
<text class="price-num">{{ item.price }}</text>
<text class="sales-text">已售{{ item.sales }}</text>
<image class="guess-img" :src="item.image" mode="aspectFill" />
<text class="guess-name" :lines="2">{{ item.name }}</text>
<view class="guess-bottom">
<text class="guess-price">¥{{ item.price }}</text>
<view class="guess-add-btn" @click.stop="addToCart(item)">
<text class="guess-add-icon">+</text>
</view>
</view>
</view>
@@ -206,21 +203,11 @@
@click="viewProductDetail(product)"
>
<image class="product-image" :src="product.image" mode="aspectFill" />
<view class="product-info">
<text class="product-name">{{ product.name }}</text>
<view class="product-tags-row" v-if="product.tag">
<text class="product-tag">{{ product.tag }}</text>
</view>
<text class="product-spec">{{ product.specification }}</text>
<view class="product-bottom">
<view class="price-box">
<text class="price-symbol">¥</text>
<text class="price-value">{{ product.price }}</text>
</view>
<view class="add-cart-btn" @click.stop="addToCart(product)">
<text class="cart-icon">+</text>
</view>
<text class="product-name" :lines="2">{{ product.name }}</text>
<view class="product-bottom">
<text class="product-price">¥{{ product.price }}</text>
<view class="product-add-btn" @click.stop="addToCart(product)">
<text class="add-icon">+</text>
</view>
</view>
</view>
@@ -278,6 +265,7 @@ type GuessItemType = {
price: number
image: string
sales: number
merchant_id: string
}
type SearchResultType = {
@@ -288,6 +276,7 @@ type SearchResultType = {
specification: string
tag: string
sales: number
merchant_id: string
}
type ShopResultType = {
@@ -377,14 +366,23 @@ const loadData = async (): Promise<void> => {
try {
loadSearchHistory()
const hotProducts = await supabaseService.getHotProducts(30)
// 获取热销商品,失败时使用空数组
let hotProducts: Product[] = []
try {
const hotResult = await supabaseService.getHotProducts(30)
hotProducts = hotResult as Product[]
} catch (hotError) {
console.error('获取热销商品失败,使用空列表:', hotError)
hotProducts = []
}
const hotList: Array<HotSearchItemType> = []
const limit1 = hotProducts.length < 10 ? hotProducts.length : 10
for (let i: number = 0; i < limit1; i++) {
const p = hotProducts[i] as UTSJSONObject
const p = hotProducts[i]
const item: HotSearchItemType = {
keyword: p.getString('name') ?? '',
keyword: p.name ?? '',
hot: true
}
hotList.push(item)
@@ -393,14 +391,15 @@ const loadData = async (): Promise<void> => {
const allItems: Array<GuessItemType> = []
for (let i: number = 0; i < hotProducts.length; i++) {
const p = hotProducts[i] as UTSJSONObject
const saleCount = p.getNumber('sale_count')
const p = hotProducts[i]
const saleCount = p.sale_count
const item: GuessItemType = {
id: p.getString('id') ?? '',
name: p.getString('name') ?? '',
price: p.getNumber('base_price') ?? 0,
image: p.getString('main_image_url') ?? '/static/default.jpg',
sales: saleCount != null ? saleCount : 0
id: p.id ?? '',
name: p.name ?? '',
price: p.base_price ?? 0,
image: p.main_image_url ?? '/static/default.jpg',
sales: saleCount != null ? saleCount : 0,
merchant_id: p.merchant_id ?? ''
}
allItems.push(item)
}
@@ -410,7 +409,8 @@ const loadData = async (): Promise<void> => {
} catch (e) {
console.error('Load data failed', e)
isError.value = true
// 不再显示错误页面,允许使用空数据
isError.value = false
}
}
@@ -517,14 +517,14 @@ const performSearch = async (): Promise<void> => {
const prodData = prodResp.data != null ? prodResp.data : []
const resultList: Array<SearchResultType> = []
for (let i: number = 0; i < prodData.length; i++) {
const p = prodData[i] as UTSJSONObject
const p = prodData[i] as Product
let tag = ''
const tagsRaw = p.get('tags')
const tagsRaw = p.tags
if (tagsRaw != null) {
try {
const tagsStr = p.getString('tags')
const tagsStr = p.tags
if (tagsStr != null) {
const tags = JSON.parse(tagsStr)
const tags = JSON.parse(tagsStr as string)
if (Array.isArray(tags) && tags.length > 0) {
const firstTag = tags[0]
tag = firstTag != null ? (firstTag as string) : ''
@@ -534,13 +534,14 @@ const performSearch = async (): Promise<void> => {
}
const searchItem: SearchResultType = {
id: p.getString('id') ?? '',
name: p.getString('name') ?? '',
image: p.getString('main_image_url') ?? '/static/default.jpg',
price: p.getNumber('base_price') ?? 0,
specification: p.getString('specification') ?? '标准规格',
id: p.id ?? '',
name: p.name ?? '',
image: p.main_image_url ?? '/static/default.jpg',
price: p.base_price ?? 0,
specification: p.specification ?? '标准规格',
tag: tag,
sales: p.getNumber('sale_count') ?? 0
sales: p.sale_count ?? 0,
merchant_id: p.merchant_id ?? ''
}
resultList.push(searchItem)
}
@@ -599,21 +600,47 @@ onMounted(() => {
})
const onInput = (e: any) => {
const eObj = e as UTSJSONObject
const detailRaw = eObj.get('detail')
const detail = detailRaw != null ? (detailRaw as UTSJSONObject) : (new UTSJSONObject())
const val = detail.getString('value') ?? ''
searchKeyword.value = val
if (val == '') {
showResults.value = false
searchSuggestions.value = []
return
try {
let val = ''
// 处理 input 事件的不同事件对象格式
if (e != null) {
// UTSJSONObject 格式 (e.detail.value)
if (e instanceof UTSJSONObject) {
const eObj = e as UTSJSONObject
const detailObj = eObj.get('detail')
if (detailObj != null && detailObj instanceof UTSJSONObject) {
const detail = detailObj as UTSJSONObject
const v = detail.get('value')
val = v != null ? (v as string) : ''
}
} else {
// 尝试转换为 UTSJSONObject
const eObj = JSON.parse(JSON.stringify(e)) as UTSJSONObject
const detailObj = eObj.get('detail')
if (detailObj != null) {
const detail = detailObj as UTSJSONObject
const v = detail.get('value')
val = v != null ? (v as string) : ''
} else {
const v = eObj.get('value')
val = v != null ? (v as string) : ''
}
}
}
searchKeyword.value = val
if (val == '') {
showResults.value = false
searchSuggestions.value = []
return
}
if (suggestTimer > 0) clearTimeout(suggestTimer)
suggestTimer = setTimeout(() => {
fetchSuggestions(val)
}, 300)
} catch (err) {
console.error('onInput error:', err)
}
if (suggestTimer > 0) clearTimeout(suggestTimer)
suggestTimer = setTimeout(() => {
fetchSuggestions(val)
}, 300)
}
const clearSearch = () => {
@@ -702,7 +729,8 @@ const loadMore = async (): Promise<void> => {
price: p.getNumber('base_price') ?? 0,
specification: p.getString('specification') ?? '标准规格',
tag: tag,
sales: p.getNumber('sale_count') ?? 0
sales: p.getNumber('sale_count') ?? 0,
merchant_id: p.getString('merchant_id') ?? ''
}
searchResults.value.push(searchItem)
}
@@ -738,11 +766,42 @@ const viewShopDetail = (shop: ShopResultType) => {
})
}
const addToCart = (product: SearchResultType | GuessItemType) => {
uni.showToast({ title: '请选择规格', icon: 'none' })
setTimeout(() => {
viewProductDetail(product)
}, 800)
const addToCart = async (product: SearchResultType | GuessItemType) => {
uni.showLoading({ title: '检查商品...' })
try {
// 统一转换为 UTSJSONObject 访问属性
const prodObj = JSON.parse(JSON.stringify(product)) as UTSJSONObject
const productId = prodObj.getString('id') ?? ''
const merchantId = prodObj.getString('merchant_id') ?? ''
// 检查商品是否有SKU
const skus = await supabaseService.getProductSkus(productId)
uni.hideLoading()
if (skus.length > 0) {
// 有规格,提示并跳转到商品详情页选择规格
uni.showToast({ title: '请选择规格', icon: 'none' })
setTimeout(() => {
uni.navigateTo({
url: '/pages/mall/consumer/product-detail?id=' + productId
})
}, 500)
} else {
// 无规格,直接加入购物车
uni.showLoading({ title: '添加中...' })
const success = await supabaseService.addToCart(productId, 1, '', merchantId)
uni.hideLoading()
if (success) {
uni.showToast({ title: '已添加到购物车', icon: 'success' })
} else {
uni.showToast({ title: '添加失败,请先登录', icon: 'none' })
}
}
} catch (e) {
console.error('添加到购物车异常', e)
uni.hideLoading()
uni.showToast({ title: '操作异常', icon: 'none' })
}
}
const openCamera = () => {
@@ -1142,64 +1201,62 @@ const goBack = () => {
}
.guess-item {
background-color: #fff;
display: flex;
flex-direction: column;
background: #fff;
border-radius: 8px;
overflow: hidden;
padding-bottom: 8px;
width: 48%;
margin-bottom: 10px;
}
.guess-img-box {
width: 100%;
height: 0;
padding-bottom: 100%;
position: relative;
background-color: #f0f0f0;
margin-bottom: 12px;
}
.guess-img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.guess-info {
padding: 8px;
height: 170px;
border-radius: 8px;
margin-bottom: 8px;
background: #f5f5f5;
}
.guess-name {
font-size: 13px;
color: #333;
margin-bottom: 6px;
margin-bottom: 5px;
line-height: 1.4;
height: 36px;
overflow: hidden;
text-overflow: ellipsis;
line-height: 1.3;
height: 34px; /* 限制2行高度 */
padding: 0 8px;
}
.guess-price-row {
.guess-bottom {
display: flex;
align-items: flex-end; /* REPLACED baseline */
flex-direction: row;
justify-content: space-between;
align-items: center;
padding: 0 8px 8px;
}
.price-symbol {
font-size: 12px;
color: #ff5000;
}
.price-num {
font-size: 16px;
.guess-price {
font-size: 15px;
color: #ff5000;
font-weight: bold;
margin-right: 6px;
}
.sales-text {
font-size: 10px;
color: #999;
.guess-add-btn {
width: 24px;
height: 24px;
background-color: #ff5000;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
}
.guess-add-icon {
color: #fff;
font-size: 16px;
font-weight: bold;
}
/* 搜索建议列表 */
@@ -1312,84 +1369,61 @@ const goBack = () => {
}
.result-item {
background-color: #fff;
border-radius: 8px;
padding: 8px;
display: flex;
flex-direction: column; /* 垂直排列 */
flex-direction: column;
background: #fff;
border-radius: 8px;
overflow: hidden;
width: 48%;
margin-bottom: 10px;
margin-bottom: 12px;
}
.product-image {
width: 100%;
height: 120px; /* 调整图片高度 */
border-radius: 4px;
background-color: #f0f0f0;
}
.product-info {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
margin-top: 8px;
height: 170px;
border-radius: 8px;
margin-bottom: 8px;
background: #f5f5f5;
}
.product-name {
font-size: 13px; /* 减小字号 */
font-size: 13px;
color: #333;
font-weight: bold;
line-height: 1.3;
height: 34px; /* 限制高度 */
margin-bottom: 5px;
line-height: 1.4;
height: 36px;
overflow: hidden;
text-overflow: ellipsis;
}
.product-tags-row {
margin-top: 2px;
display: none; /* 隐藏标签以保持简洁 */
}
.product-spec {
display: none; /* 隐藏规格 */
padding: 0 8px;
}
.product-bottom {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center; /* 垂直居中 */
margin-top: 4px;
align-items: center;
padding: 0 8px 8px;
}
.price-box {
.product-price {
font-size: 15px;
color: #ff5000;
display: flex;
align-items: flex-end;
font-weight: bold;
}
.price-symbol {
font-size: 10px;
}
.price-value {
font-size: 16px; /* 减小价格字号 */
font-weight: 700;
}
.add-cart-btn {
.product-add-btn {
width: 24px;
height: 24px;
background-color: #4CAF50;
background-color: #ff5000;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
}
.cart-icon {
.add-icon {
color: #fff;
font-size: 14px;
font-size: 16px;
font-weight: bold;
}