ry_app/package_a/addProduct/addProduct.vue

599 lines
20 KiB
Vue

<template>
<view class="add-product-page">
<!-- 头部 -->
<view class="page-header">
<text class="header-title">添加商品</text>
</view>
<!-- 表单区域 -->
<view class="form-container">
<!-- 商品名称 -->
<view class="form-item">
<text class="item-label">商品名称</text>
<input
class="item-input"
type="text"
placeholder="名称"
v-model="productInfo.name"
/>
</view>
<!-- 条码/条码扫码 -->
<view class="form-item">
<text class="item-label">条码</text>
<view class="barcode-section">
<input
class="item-input barcode-input"
type="text"
placeholder="条码/条码扫码"
v-model="productInfo.barcode"
/>
<view class="scan-btn" @tap="handleScan">
<text class="scan-text">扫码</text>
</view>
</view>
</view>
<!-- 商品图 -->
<view class="form-item">
<text class="item-label">商品图</text>
<view class="image-upload-section">
<view class="upload-placeholder" @tap="chooseImage">
<text class="upload-icon">+</text>
<text class="upload-text">点击上传商品图片</text>
</view>
<image
v-if="productInfo.image"
:src="productInfo.image"
class="product-image"
mode="aspectFit"
></image>
</view>
</view>
<view class="divider"></view>
<!-- 售价 -->
<view class="form-item">
<text class="item-label">售价</text>
<view class="price-section">
<input
class="item-input price-input"
type="digit"
placeholder="5.0"
v-model="productInfo.price"
/>
<text class="price-unit">元</text>
</view>
</view>
<!-- 推荐价格 -->
<view class="recommend-price-section">
<text class="recommend-title">推荐价格(点击快速设置售价)</text>
<view class="recommend-list">
<view class="recommend-item" @tap="setPrice(5.0)">
<text class="recommend-percent">82%商家卖</text>
<text class="recommend-price">5.0元</text>
</view>
<view class="recommend-item" @tap="setPrice(5.5)">
<text class="recommend-percent">9%商家卖</text>
<text class="recommend-price">5.5元</text>
</view>
<view class="recommend-item" @tap="setPrice(4.5)">
<text class="recommend-percent">2%商家卖</text>
<text class="recommend-price">4.5元</text>
</view>
</view>
</view>
<view class="divider"></view>
<!-- 进货数量 -->
<view class="form-item">
<text class="item-label">进货数量</text>
<view class="quantity-section">
<input
class="item-input"
type="number"
placeholder="请输入进货数量"
v-model="productInfo.quantity"
/>
<text class="quantity-unit">件</text>
</view>
</view>
<!-- 进货价 -->
<view class="form-item">
<text class="item-label">进货价</text>
<view class="cost-section">
<input
class="item-input"
type="digit"
placeholder="请输入进货价"
v-model="productInfo.cost"
/>
<text class="cost-unit">元</text>
</view>
</view>
<!-- ✅ 保质期管理 - 红色滑动开关 完美匹配截图 -->
<view class="form-item">
<text class="item-label">保质期管理</text>
<switch
:checked="productInfo.expirationManagement"
@change="toggleExpirationManagement"
color="#F53F3F"
/>
</view>
<!-- 保质期天数 -->
<view class="form-item" v-if="productInfo.expirationManagement">
<text class="item-label">保质期天数</text>
<view class="expiration-options">
<view
class="expiration-item"
:class="{ active: productInfo.expirationDays === 365 }"
@tap="setExpirationDays(365)"
>
<text class="expiration-text">1年</text>
<text class="expiration-days">36天</text>
</view>
<view
class="expiration-item"
:class="{ active: productInfo.expirationDays === 270 }"
@tap="setExpirationDays(270)"
>
<text class="expiration-text">9个月</text>
<text class="expiration-days">27天</text>
</view>
<view
class="expiration-item"
:class="{ active: productInfo.expirationDays === 180 }"
@tap="setExpirationDays(180)"
>
<text class="expiration-text">6个月</text>
<text class="expiration-days">18天</text>
</view>
<view
class="expiration-item"
:class="{ active: productInfo.expirationDays === 240 }"
@tap="setExpirationDays(240)"
>
<text class="expiration-text">8个月</text>
<text class="expiration-days">24天</text>
</view>
<view
class="expiration-item"
:class="{ active: productInfo.expirationDays === 300 }"
@tap="setExpirationDays(300)"
>
<text class="expiration-text">10个月</text>
<text class="expiration-days">30天</text>
</view>
</view>
</view>
<!-- 临期提醒天数 -->
<view class="form-item" v-if="productInfo.expirationManagement">
<text class="item-label">临期提醒天数</text>
<view class="warning-days-display">
<text class="warning-days-value">{{ productInfo.shelfLife }}</text>
<text class="warning-days-unit">天</text>
</view>
</view>
<!-- 生产日期 -->
<view class="form-item" v-if="productInfo.expirationManagement">
<text class="item-label">生产日期</text>
<uni-datetime-picker
type="date"
v-model="productInfo.productionDate"
:start="startDate"
:end="endDate"
placeholder="请选择生产日期"
/>
</view>
<!-- 货架码 -->
<view class="form-item">
<text class="item-label">货架码</text>
<input
class="item-input"
type="text"
placeholder="请输入货架码"
v-model="productInfo.shelfCode"
/>
</view>
<!-- 商品编码 -->
<view class="form-item">
<text class="item-label">商品编码</text>
<input
class="item-input"
type="text"
placeholder="请输入商品编码"
v-model="productInfo.productCode"
/>
</view>
</view>
<!-- 保存按钮 -->
<view class="save-button-container">
<button class="save-button" @tap="handleSave">保存</button>
</view>
<!-- 微信小程序全屏扫码覆盖层 -->
<!-- #ifdef MP-WEIXIN -->
<view v-if="showScanner" class="scanner-overlay">
<camera
class="scanner-camera"
mode="scanCode"
device-position="back"
flash="off"
@scancode="onOverlayScanResult"
>
<cover-view class="scanner-mask">
<cover-view class="scanner-box">
<cover-view class="box-corner tl"></cover-view>
<cover-view class="box-corner tr"></cover-view>
<cover-view class="box-corner bl"></cover-view>
<cover-view class="box-corner br"></cover-view>
</cover-view>
<cover-view class="scanner-actions">
<cover-view class="scanner-btn" @tap="closeScanner"></cover-view>
</cover-view>
</cover-view>
</camera>
</view>
<!-- #endif -->
</view>
</template>
<script>
import { addProductWithFile } from '@/api/product'
import { getStoreId } from '@/utils/auth'
import apiUrl from '@/utils/api'
export default {
data() {
return {
productInfo: {
name: '',
barcode: '',
image: '',
price: '',
quantity: '',
cost: '',
expirationManagement: false,
shelfCode: '',
shelfLife:'',
productCode: '',
expirationDays: '',
warningDays: '',
productionDate: '',
url:''
},
showScanner: false
}
},
computed: {
warningDaysDisplay() {
if (!this.productInfo.expirationDays) return '';
const days = parseInt(this.productInfo.expirationDays);
return days.toString().substring(0, 2);
},
startDate() {
return '2020-01-01';
},
endDate() {
const now = new Date();
const year = now.getFullYear();
const month = (now.getMonth() + 1).toString().padStart(2, '0');
const day = now.getDate().toString().padStart(2, '0');
return `${year}-${month}-${day}`;
}
},
onLoad(options) {
console.log('页面参数:', options,apiUrl);
this.url = apiUrl
// 从扫码页面传入的条形码
if (options && options.fromData) {
try {
this.productInfo.barcode = options.barcode
// 使用 decodeURIComponent 解码乱码数据
const decodedData = decodeURIComponent(options.fromData);
console.log('解码数据',decodedData);
const fromData = JSON.parse(decodedData)[0];
this.productInfo.name = fromData.productName || '';
this.productInfo.cost = fromData.costPrice || '';
this.productInfo.barcode = fromData.productBarCode || '';
this.productInfo.price = fromData.storePrice || '';
this.productInfo.quantity = fromData.stockQuantity || '';
this.productInfo.productCode = fromData.productCode || '';
this.productInfo.image = this.url + fromData.mainImage || '';
this.productInfo.shelfCode = fromData.shelfCode || '';
this.productInfo.expirationManagement = fromData.shelfLife?true:false;
if(fromData.shelfLife){
console.log(fromData.productionDate);
this.productInfo.productionDate = fromData.productionDate || '2026-12-19'
this.productInfo.shelfLife = fromData.shelfLife;
}
console.log(this.productInfo);
} catch (e) {
this.productInfo.barcode = options.barcode
}
}
if (options.fromData) {
try {
} catch (error) {
console.error('解析商品数据失败:', error);
}
}
},
methods: {
handleScan() {
// #ifdef MP-WEIXIN
this.showScanner = true
// #endif
// #ifndef MP-WEIXIN
uni.scanCode({
onlyFromCamera: true,
scanType: ['barCode'],
success: (res) => {
const code = (res.result || '').trim()
const type = (res.scanType || res.codeType || '').toString().toUpperCase()
const isBarcode = type.indexOf('QR') === -1
const numericOk = /^\d{8,20}$/.test(code)
if (isBarcode || numericOk) {
this.productInfo.barcode = code
uni.showToast({ title: '扫码成功', icon: 'success' })
} else {
uni.showToast({ title: '请扫条形码', icon: 'none' })
}
},
fail: () => {
uni.showToast({ title: '扫码失败', icon: 'none' })
}
})
// #endif
},
closeScanner() {
this.showScanner = false
},
onOverlayScanResult(e) {
if (!this.showScanner) return
const { result, scanType } = e.detail || {}
const code = (result || '').trim()
const type = (scanType || '').toString().toUpperCase()
const isBarcode = type.indexOf('QR') === -1
const numericOk = /^\d{8,20}$/.test(code)
if (isBarcode || numericOk) {
this.productInfo.barcode = code
this.showScanner = false
uni.showToast({ title: '扫码成功', icon: 'success' })
} else {
uni.showToast({ title: '请扫条形码', icon: 'none' })
}
},
// 选择图片
chooseImage() {
uni.chooseImage({
count: 1,
sizeType: ['compressed'],
sourceType: ['album', 'camera'],
success: (res) => {
this.productInfo.image = res.tempFilePaths[0]
}
})
},
// 设置推荐价格
setPrice(price) {
this.productInfo.price = price.toString()
uni.showToast({ title: `已设置为${price}`, icon: 'success', duration: 1500 })
},
// ✅ 切换保质期管理-适配红色滑动开关 极简写法
toggleExpirationManagement(e) {
this.productInfo.expirationManagement = e.detail.value
},
// 设置保质期天数
setExpirationDays(days) {
this.productInfo.expirationDays = days.toString()
const warningDays = days.toString().substring(0, 2);
this.productInfo.warningDays = warningDays;
uni.showToast({ title: `已设置为${days}`, icon: 'success', duration: 1500 })
},
// 表单验证-完整校验
validateForm() {
if (!this.productInfo.name.trim()) { uni.showToast({ title: '请输入商品名称', icon: 'none' });return false }
if (!this.productInfo.price) { uni.showToast({ title: '请输入售价', icon: 'none' });return false }
// 开启保质期管理 必校验三项
if (this.productInfo.expirationManagement) {
if (!this.productInfo.shelfLife) { uni.showToast({ title: '请选择保质期天数', icon: 'none' });return false }
// 临期提醒天数自动设置,不需要手动输入
if (!this.productInfo.productionDate) { uni.showToast({ title: '请选择生产日期', icon: 'none' });return false }
}
return true
},
// 保存商品-完整传参
async handleSave() {
if (!this.validateForm()) return
const storeId = getStoreId();
if (!storeId) { uni.showToast({ title: '请先选择门店', icon: 'none' });return }
uni.showLoading({ title: '保存中...' });
try {
const formData = {
productName: this.productInfo.name,
productBarCode: this.productInfo.barcode,
storePrice: this.productInfo.price,
stockQuantity: this.productInfo.quantity,
costPrice: this.productInfo.costPrice,
productCode: this.productInfo.productCode,
shelfCode: this.productInfo.shelfCode,
expirationManagement: this.productInfo.expirationManagement ? 1 : 0,
shelfLife: this.productInfo.expirationDays,
productionDate: this.productInfo.productionDate,
approaching: this.productInfo.warningDays,
storeId: storeId
};
const res = await addProductWithFile(this.productInfo.image || '', formData);
uni.hideLoading();
if (res.code === 200) {
uni.showToast({ title: '商品添加成功', icon: 'success', duration: 2000 });
setTimeout(() => { uni.navigateBack({ delta: 1 }); }, 2000);
} else {
uni.showToast({ title: res.msg || '商品添加失败', icon: 'none' });
}
} catch (error) {
uni.hideLoading();
console.error('添加商品失败:', error);
uni.showToast({ title: '网络请求失败', icon: 'none' });
}
}
}
}
</script>
<style scoped>
.add-product-page {
background-color: #f5f5f5;
min-height: 100vh;
padding-bottom: 120rpx;
}
/* 头部 */
.page-header {
background-color: #ffffff;
padding: 40rpx 32rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.header-title { font-size: 36rpx;font-weight: 600;color: #333333; }
/* 表单容器 */
.form-container { background-color: #ffffff;margin-top: 20rpx; }
/* 表单项 */
.form-item {
padding: 32rpx;
border-bottom: 1rpx solid #f0f0f0;
display: flex;
align-items: center;
justify-content: space-between;
}
.item-label { font-size: 32rpx;color: #333333;font-weight: 500;min-width: 160rpx; }
.item-input { flex:1;font-size:32rpx;color:#333;text-align:right;padding:0 20rpx; }
.item-input::placeholder { color: #999999; }
/* 条码区域 */
.barcode-section { display:flex;align-items:center;flex:1; }
.barcode-input { flex:1;margin-right:20rpx; }
.scan-btn { background:#07C160;padding:16rpx 32rpx;border-radius:8rpx; }
.scan-text { color:#fff;font-size:28rpx; }
/* 图片上传区域 */
.image-upload-section { flex:1;display:flex;justify-content:flex-end; }
.upload-placeholder { width:200rpx;height:200rpx;border:2rpx dashed #ccc;border-radius:8rpx;display:flex;flex-direction:column;align-items:center;justify-content:center;background:#f9f9f9; }
.upload-icon { font-size:48rpx;color:#999;margin-bottom:16rpx; }
.upload-text { font-size:24rpx;color:#999; }
.product-image { width:200rpx;height:200rpx;border-radius:8rpx; }
/* 价格区域 */
.price-section { display:flex;align-items:center;flex:1;justify-content:flex-end; }
.price-input { flex:none;width:120rpx;text-align:center; }
.price-unit { font-size:32rpx;color:#333;margin-left:10rpx; }
/* 推荐价格 */
.recommend-price-section { padding:32rpx;border-bottom:1rpx solid #f0f0f0; }
.recommend-title { font-size:28rpx;color:#666;margin-bottom:24rpx;display:block; }
.recommend-list { display:flex;justify-content:space-between; }
.recommend-item { display:flex;flex-direction:column;align-items:center;padding:20rpx 24rpx;background:#f9f9f9;border-radius:8rpx;flex:1;margin:0 10rpx; }
.recommend-percent { font-size:26rpx;color:#666;margin-bottom:8rpx; }
.recommend-price { font-size:30rpx;color:#07C160;font-weight:500; }
/* 数量/进货价 单位 */
.quantity-section,.cost-section { display:flex;align-items:center;flex:1;justify-content:flex-end; }
.quantity-unit,.cost-unit { font-size:32rpx;color:#333;margin-left:10rpx; }
/* ✅ 保质期相关样式 完美匹配 */
.expiration-options { display:flex;flex-wrap:wrap;gap:20rpx;flex:1;justify-content:flex-end; }
.expiration-item { display:flex;flex-direction:column;align-items:center;padding:16rpx 20rpx;background:#f9f9f9;border-radius:8rpx;min-width:100rpx;border:2rpx solid transparent;transition:all 0.3s; }
.expiration-item.active { background:#F53F3F;border-color:#F53F3F; }
.expiration-item.active .expiration-text { color:#fff; }
.expiration-item.active .expiration-days { color:#fff; }
.expiration-text { font-size:28rpx;color:#333; }
.expiration-days { font-size:24rpx;color:#999;margin-top:4rpx; }
/* 临期提醒天数样式 */
.warning-days-display { display:flex;align-items:center;flex:1;justify-content:flex-end; }
.warning-days-value { font-size:36rpx;color:#F53F3F;font-weight:600;margin-right:8rpx; }
.warning-days-unit { font-size:28rpx;color:#666; }
/* 分隔线 */
.divider { height:20rpx;background:#f5f5f5;border-top:1rpx solid #f0f0f0;border-bottom:1rpx solid #f0f0f0; }
/* 保存按钮 */
.save-button-container { position:fixed;bottom:0;left:0;right:0;background:#fff;padding:20rpx 32rpx;border-top:1rpx solid #f0f0f0; }
.save-button { background:#F53F3F;color:#ffffff;font-size:34rpx;height:88rpx;line-height:88rpx;border-radius:8rpx; }
.save-button::after { border:none; }
/* 响应式调整 */
@media (max-width:750px) {
.recommend-list { flex-direction:column; }
.recommend-item { margin:10rpx 0; }
}
/* 微信小程序全屏扫码覆盖层样式 */
.scanner-overlay {
position: fixed;
left: 0; right: 0; top: 0; bottom: 0;
background: #000;
z-index: 9999;
}
.scanner-camera {
width: 100%;
height: 100%;
}
.scanner-mask {
position: absolute;
left: 0; top: 0; right: 0; bottom: 0;
}
.scanner-box {
position: fixed;
left: 50%;
top: 50%;
width: 520rpx;
height: 520rpx;
transform: translate(-50%, -50%);
}
.box-corner {
position: absolute;
width: 40rpx;
height: 40rpx;
border: 4rpx solid #fff;
}
.box-corner.tl { left: 0; top: 0; border-right: none; border-bottom: none; }
.box-corner.tr { right: 0; top: 0; border-left: none; border-bottom: none; }
.box-corner.bl { left: 0; bottom: 0; border-right: none; border-top: none; }
.box-corner.br { right: 0; bottom: 0; border-left: none; border-top: none; }
.scanner-actions {
position: absolute;
left: 0; right: 0; bottom: 80rpx;
display: flex; justify-content: center;
}
.scanner-btn {
padding: 20rpx 40rpx;
color: #fff;
border: 2rpx solid rgba(255,255,255,0.6);
border-radius: 40rpx;
background: rgba(0,0,0,0.35);
}
</style>