Files
solosw 99b11b04e4 1.0
2026-01-05 14:11:34 +08:00

672 lines
14 KiB
Vue
Raw Permalink 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="container">
<view class="warehouse-list">
<view class="warehouse-item" v-for="(item, index) in warehouseList" :key="item.id">
<view class="product-card">
<image :src="getFullImageUrl(getFirstImage(item.product_images))"
class="product-image"
mode="aspectFill"
@error="onImageError"></image>
<view v-if="!getFirstImage(item.product_images)" class="default-product">
<uni-icons type="image" size="60" color="#666"></uni-icons>
</view>
</view>
<view class="product-info">
<text class="product-name">{{ item.product_name }}</text>
<view class="product-stats">
<text class="stats-text">库存 {{ item.quantity }}</text>
<text class="stats-text">{{ getProductTypeText(item.product_type) }}</text>
<text class="stats-text" v-if="item.status === 0">已出售</text>
</view>
<view class="product-price">
<text class="purchase-price">购买价:¥{{ (item.purchase_price * item.quantity).toFixed(2) }}</text>
<text class="current-price">当前价值:¥{{ (item.warehouse_price * item.quantity).toFixed(2) }}</text>
</view>
</view>
<view class="product-actions">
<button v-if="item.status === 1"
class="action-btn sell-btn"
@click="sellProduct(item)">
立即挂售
</button>
<button v-else-if="item.status === 0"
class="action-btn disabled-btn"
disabled>
不可出售
</button>
<button v-else
class="action-btn sold-btn"
disabled>
已出售
</button>
</view>
</view>
</view>
<!-- 空状态 -->
<view class="empty-state" v-if="warehouseList.length === 0 && !loading">
<uni-icons type="home" size="100" color="#ccc"></uni-icons>
<text class="empty-text">仓库暂无商品</text>
</view>
<!-- 底部提示 -->
<view class="bottom-tip" v-if="warehouseList.length > 0">
<text class="tip-text">没有更多了</text>
</view>
<!-- 挂售弹窗 -->
<uni-popup ref="sellPopup" type="bottom" border-radius="10px 10px 0 0">
<view class="sell-popup">
<view class="popup-header">
<text class="popup-title">商品挂售</text>
<uni-icons type="close" @click="closeSellPopup" class="close-btn"></uni-icons>
</view>
<view class="popup-content" v-if="selectedItem">
<view class="product-summary">
<image :src="getFullImageUrl(getFirstImage(selectedItem.product_images))"
class="summary-image"
mode="aspectFill"></image>
<view class="summary-info">
<text class="summary-name">{{ selectedItem.product_name }}</text>
<text class="summary-stock">库存{{ selectedItem.quantity }}</text>
<text class="summary-price">仓库价值:¥{{ selectedItem.warehouse_price }}/</text>
</view>
</view>
<view class="sell-form">
<view class="form-item">
<text class="form-label">挂售数量</text>
<view class="quantity-selector">
<button class="quantity-btn" @click="decreaseQuantity" :disabled="sellQuantity <= 1">-</button>
<input type="number" v-model.number="sellQuantity" class="quantity-input" :max="selectedItem.quantity" min="1">
<button class="quantity-btn" @click="increaseQuantity" :disabled="sellQuantity >= selectedItem.quantity">+</button>
</view>
</view>
<view class="form-item">
<text class="form-label">出售价格</text>
<view class="price-input-wrap">
<text class="price-symbol">¥</text>
<input disabled type="digit" v-model="sellPrice" class="price-input" placeholder="请输入出售价格">
</view>
</view>
<view class="form-item">
<text class="form-label">商品描述</text>
<textarea v-model="sellDescription" class="description-input" placeholder="请描述商品的具体情况..." maxlength="500"></textarea>
</view>
<!-- <view class="form-item">
<text class="form-label">商品成色</text>
<view class="condition-selector">
<view class="condition-item"
v-for="condition in conditionOptions"
:key="condition.value"
:class="{ active: sellCondition === condition.value }"
@click="sellCondition = condition.value">
<text>{{ condition.label }}</text>
</view>
</view>
</view> -->
</view>
<view class="sell-summary">
<text class="summary-text">挂售 {{ sellQuantity }} 总价值 ¥{{ (sellPrice * sellQuantity).toFixed(2) }}</text>
</view>
</view>
<view class="popup-footer">
<button class="cancel-btn" @click="closeSellPopup">取消</button>
<button class="confirm-btn" @click="confirmSell" :disabled="!canSubmit">确认挂售</button>
</view>
</view>
</uni-popup>
</view>
</template>
<script>
import request from '@/utils/request.js'
export default {
data() {
return {
warehouseList: [],
loading: false,
// 挂售相关数据
selectedItem: null,
sellQuantity: 1,
sellPrice: '',
sellDescription: '',
sellCondition: '九成新',
conditionOptions: [
{ label: '全新', value: '全新' },
{ label: '九成新', value: '九成新' },
{ label: '八成新', value: '八成新' },
{ label: '七成新', value: '七成新' },
{ label: '六成新', value: '六成新' },
{ label: '其他', value: '其他' }
],
submitting: false
}
},
computed: {
canSubmit() {
return this.sellQuantity > 0 &&
this.sellQuantity <= (this.selectedItem?.quantity || 0) &&
this.sellPrice > 0 &&
this.sellDescription.trim() &&
!this.submitting
}
},
onLoad() {
// 检查登录状态
const token = uni.getStorageSync('token')
if (!token) {
uni.reLaunch({
url: '/pages/login/login'
})
return
}
this.loadWarehouse()
},
onShow() {
this.loadWarehouse()
},
methods: {
// 获取完整的图片URL
getFullImageUrl(url) {
return request.getImageUrl(url)
},
// 获取商品的第一张图片
getFirstImage(images) {
if (!images) return ''
if (typeof images === 'string') {
return images.split(',')[0].trim()
}
if (Array.isArray(images) && images.length > 0) {
return images[0]
}
return ''
},
// 获取商品类型文本
getProductTypeText(type) {
return type === 1 ? '一级商品' : '二级商品'
},
// 图片加载错误处理
onImageError() {
console.log('图片加载失败')
},
// 加载仓库数据
async loadWarehouse() {
this.loading = true
try {
const res = await request.get('/back/user/warehouse', {
page: 1,
page_size: 50
})
if (res.success) {
this.warehouseList = res.data.list || []
}
} catch (error) {
console.error('加载仓库失败:', error)
uni.showToast({
title: '加载失败',
icon: 'none'
})
} finally {
this.loading = false
}
},
// 挂售商品
sellProduct(item) {
if (item.quantity <= 0) {
uni.showToast({
title: '库存不足,无法挂售',
icon: 'none'
})
return
}
// 设置选中商品并重置表单
this.selectedItem = item
this.sellQuantity = 1
this.sellPrice = item.warehouse_price.toString()
this.sellDescription = `${item.product_name} - 来自个人仓库`
this.sellCondition = '九成新'
this.submitting = false
// 打开挂售弹窗
this.$refs.sellPopup.open()
},
// 关闭挂售弹窗
closeSellPopup() {
this.$refs.sellPopup.close()
this.selectedItem = null
},
// 减少数量
decreaseQuantity() {
if (this.sellQuantity > 1) {
this.sellQuantity--
}
},
// 增加数量
increaseQuantity() {
if (this.sellQuantity < this.selectedItem.quantity) {
this.sellQuantity++
}
},
// 确认挂售
async confirmSell() {
if (!this.canSubmit) return
this.submitting = true
try {
const sellData = {
quantity: this.sellQuantity,
price: parseFloat(this.sellPrice),
description: this.sellDescription.trim(),
condition: this.sellCondition
}
const res = await request.post(`/back/user/warehouse/${this.selectedItem.id}/sell`, sellData)
if (res.success) {
uni.showToast({
title: '挂售成功',
icon: 'success'
})
this.closeSellPopup()
this.loadWarehouse() // 重新加载仓库数据
} else {
throw new Error(res.message || '挂售失败')
}
} catch (error) {
console.error('挂售失败:', error)
uni.showToast({
title: error.message || '挂售失败',
icon: 'none'
})
} finally {
this.submitting = false
}
}
}
}
</script>
<style scoped>
.container {
background-color: #f5f5f5;
min-height: 100vh;
padding: 20rpx;
}
.warehouse-list {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20rpx;
}
.warehouse-item {
background-color: #fff;
border-radius: 16rpx;
padding: 20rpx;
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.1);
}
.product-card {
width: 100%;
height: 200rpx;
border-radius: 12rpx;
overflow: hidden;
margin-bottom: 20rpx;
background-color: #333;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
position: relative;
}
.product-image {
width: 100%;
height: 100%;
}
.default-product {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background-color: #f5f5f5;
}
.product-info {
margin-bottom: 20rpx;
}
.product-name {
display: block;
font-size: 28rpx;
font-weight: bold;
color: #333;
margin-bottom: 15rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.product-stats {
margin-bottom: 15rpx;
}
.stats-text {
display: block;
font-size: 24rpx;
color: #999;
margin-bottom: 8rpx;
}
.product-price {
margin-bottom: 10rpx;
}
.current-price {
font-size: 32rpx;
font-weight: bold;
color: #FF4444;
}
.product-actions {
text-align: center;
}
.action-btn {
width: 100%;
padding: 20rpx;
border-radius: 8rpx;
font-size: 24rpx;
border: none;
}
.sell-btn {
background-color: #FF4444;
color: #fff;
}
.disabled-btn {
background-color: #f5f5f5;
color: #999;
}
.sold-btn {
background-color: #f0f0f0;
color: #ccc;
}
.purchase-price {
font-size: 24rpx;
color: #999;
margin-bottom: 8rpx;
}
/* 空状态 */
.empty-state {
text-align: center;
padding: 200rpx 0;
grid-column: 1 / -1;
}
.empty-state uni-icons {
margin-bottom: 40rpx;
}
.empty-text {
font-size: 28rpx;
color: #999;
}
/* 底部提示 */
.bottom-tip {
text-align: center;
padding: 40rpx;
color: #999;
font-size: 24rpx;
grid-column: 1 / -1;
}
/* 挂售弹窗样式 */
.sell-popup {
background-color: #fff;
border-radius: 20rpx 20rpx 0 0;
padding: 0;
max-height: 80vh;
display: flex;
flex-direction: column;
}
.popup-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx;
border-bottom: 1rpx solid #f5f5f5;
}
.popup-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
}
.close-btn {
padding: 10rpx;
color: #999;
}
.popup-content {
flex: 1;
padding: 30rpx;
overflow-y: auto;
}
.product-summary {
display: flex;
margin-bottom: 40rpx;
padding: 20rpx;
background-color: #f8f9fa;
border-radius: 12rpx;
}
.summary-image {
width: 120rpx;
height: 120rpx;
border-radius: 8rpx;
margin-right: 20rpx;
}
.summary-info {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.summary-name {
font-size: 28rpx;
font-weight: bold;
color: #333;
margin-bottom: 8rpx;
}
.summary-stock,
.summary-price {
font-size: 24rpx;
color: #666;
margin-bottom: 4rpx;
}
.sell-form {
margin-bottom: 30rpx;
}
.form-item {
margin-bottom: 30rpx;
}
.form-label {
display: block;
font-size: 28rpx;
color: #333;
margin-bottom: 15rpx;
font-weight: 500;
}
.quantity-selector {
display: flex;
align-items: center;
justify-content: center;
}
.quantity-btn {
width: 60rpx;
height: 60rpx;
border: 1rpx solid #ddd;
background-color: #fff;
color: #333;
font-size: 32rpx;
border-radius: 8rpx;
display: flex;
align-items: center;
justify-content: center;
}
.quantity-btn:disabled {
background-color: #f5f5f5;
color: #ccc;
}
.quantity-input {
width: 120rpx;
height: 60rpx;
text-align: center;
border: 1rpx solid #ddd;
border-left: none;
border-right: none;
font-size: 28rpx;
background-color: #fff;
}
.price-input-wrap {
display: flex;
align-items: center;
border: 1rpx solid #ddd;
border-radius: 8rpx;
padding: 0 20rpx;
background-color: #fff;
}
.price-symbol {
font-size: 28rpx;
color: #666;
margin-right: 10rpx;
}
.price-input {
flex: 1;
height: 80rpx;
font-size: 28rpx;
border: none;
background: none;
}
.description-input {
width: 100%;
min-height: 120rpx;
padding: 20rpx;
border: 1rpx solid #ddd;
border-radius: 8rpx;
font-size: 28rpx;
line-height: 1.5;
background-color: #fff;
}
.condition-selector {
display: flex;
flex-wrap: wrap;
gap: 15rpx;
}
.condition-item {
padding: 15rpx 25rpx;
border: 1rpx solid #ddd;
border-radius: 30rpx;
font-size: 24rpx;
color: #666;
background-color: #fff;
}
.condition-item.active {
border-color: #FF4444;
background-color: #FF4444;
color: #fff;
}
.sell-summary {
padding: 20rpx;
background-color: #f8f9fa;
border-radius: 8rpx;
text-align: center;
}
.summary-text {
font-size: 28rpx;
color: #333;
font-weight: 500;
}
.popup-footer {
display: flex;
padding: 30rpx;
border-top: 1rpx solid #f5f5f5;
gap: 20rpx;
}
.cancel-btn,
.confirm-btn {
flex: 1;
height: 80rpx;
border-radius: 8rpx;
font-size: 28rpx;
border: none;
}
.cancel-btn {
background-color: #f5f5f5;
color: #666;
}
.confirm-btn {
background-color: #FF4444;
color: #fff;
}
.confirm-btn:disabled {
background-color: #ccc;
color: #999;
}
</style>