ry_app/pages/edit/edit.vue

892 lines
24 KiB
Vue
Raw Normal View History

2026-01-19 16:52:24 +08:00
<template>
<view class="goods-edit-page">
<!-- 顶部导航栏 -->
<view class="navbar">
<view class="nav-left" @click="navBack">
<text class="icon"></text>
</view>
<view class="nav-title">编辑商品</view>
<view class="nav-right">
<text class="icon">···</text>
</view>
</view>
<!-- 商品图片展示区 -->
<view class="goods-img-box">
<image
class="goods-img"
:src="goodsInfo.imageUrl"
mode="widthFix"
@click="previewImage"
></image>
<view class="change-img-btn" @click="chooseImage"></view>
</view>
<!-- 核心表单区域 -->
<view class="form-container">
<!-- 商品名称 -->
<view class="form-item">
<view class="form-label">
<text class="required">*</text>名称
</view>
<input
class="item-input"
type="text"
placeholder="请输入商品名称"
v-model="goodsInfo.name"
/>
</view>
<!-- 商品单位 -->
<view class="form-item">
<view class="form-label">商品单位</view>
<input
class="item-input"
type="text"
placeholder="请输入商品单位"
v-model="goodsInfo.unit"
/>
</view>
<!-- 条码+扫码按钮 -->
<view class="form-item">
<view class="form-label">
<text class="required">*</text>条码
</view>
<view class="flex-row">
<input
class="item-input barcode-input"
type="text"
placeholder="请输入条码"
v-model="goodsInfo.barcode"
/>
<view class="scan-btn" @click="scanBarcode"></view>
</view>
</view>
<!-- 售价核心区域修正排版 -->
<view class="form-item">
<view class="form-label">
<text class="required">*</text>售价
</view>
<view class="flex-row">
<input
class="item-input price-input"
type="digit"
placeholder="请输入售价"
v-model="goodsInfo.salePrice"
/>
<text class="price-unit"></text>
</view>
</view>
<!-- 推荐价格提示与选项 -->
<view class="price-recommend-section">
<view class="price-tips">
推荐价格点击快速设置售价<text class="tips-icon">i</text>
</view>
<view class="price-option-list">
<view
class="price-option"
:class="{active: activePrice == item.price}"
v-for="item in priceOptions"
:key="item.price"
@click="setSalePrice(item.price)"
>
<text class="option-text">{{ item.desc }}</text>
<text class="option-price">{{ item.price }}</text>
</view>
</view>
</view>
<!-- 库存 -->
<view class="form-item">
<view class="form-label">库存</view>
<view class="flex-row">
<input
class="item-input"
type="number"
placeholder="请输入库存"
v-model="goodsInfo.stock"
/>
<text class="unit-text"></text>
</view>
</view>
<!-- 最近进货价 -->
<view class="form-item">
<view class="form-label">最近进货价</view>
<view class="flex-row">
<input
class="item-input"
type="digit"
placeholder="请输入进货价"
v-model="goodsInfo.buyPrice"
/>
<text class="unit-text"></text>
</view>
</view>
<!-- 货架码 -->
<view class="form-item">
<view class="form-label">货架码</view>
<input
class="item-input"
type="text"
placeholder="请输入货架码"
v-model="goodsInfo.shelfCode"
/>
</view>
<!-- 添加更多单位按钮 -->
<view class="add-unit-btn" @click="addUnit">+ </view>
</view>
<!-- 其他设置区域 -->
<view class="settings-container">
<view class="settings-title">其他设置</view>
<!-- 保质期管理 -->
<view class="form-item">
<text class="item-label">保质期管理</text>
<switch
:checked="switchStatus.expire"
@change="toggleExpirationManagement"
color="#F53F3F"
/>
</view>
<!-- 保质期天数 -->
<view class="form-item" v-if="switchStatus.expire">
<text class="item-label">保质期天数</text>
<view class="expiration-options">
<view
class="expiration-item"
:class="{ active: expireSettings.days === 365 }"
@tap="setExpirationDays(365)"
>
<text class="expiration-text">1</text>
<text class="expiration-days">365</text>
</view>
<view
class="expiration-item"
:class="{ active: expireSettings.days === 270 }"
@tap="setExpirationDays(270)"
>
<text class="expiration-text">9个月</text>
<text class="expiration-days">270</text>
</view>
<view
class="expiration-item"
:class="{ active: expireSettings.days === 180 }"
@tap="setExpirationDays(180)"
>
<text class="expiration-text">6个月</text>
<text class="expiration-days">180</text>
</view>
<view
class="expiration-item"
:class="{ active: expireSettings.days === 240 }"
@tap="setExpirationDays(240)"
>
<text class="expiration-text">8个月</text>
<text class="expiration-days">240</text>
</view>
<view
class="expiration-item"
:class="{ active: expireSettings.days === 300 }"
@tap="setExpirationDays(300)"
>
<text class="expiration-text">10个月</text>
<text class="expiration-days">300</text>
</view>
</view>
</view>
<!-- 临期提醒天数 -->
<view class="form-item" v-if="switchStatus.expire">
<text class="item-label">临期提醒天数</text>
<view class="flex-row">
<input
class="item-input"
type="number"
placeholder="请输入临期提醒天数"
v-model="expireSettings.warnDays"
/>
<text class="unit-text"></text>
</view>
</view>
<!-- 未成年人购买香烟提示 -->
<view class="setting-item flex-row-between">
<view class="setting-label flex-row">
<text>未成年人购买香烟提示</text>
<text class="tips-icon">i</text>
</view>
<view class="switch-box" @click="toggleSwitch('minor')">
<view class="switch-bg" :class="{open: switchStatus.minor}"></view>
<view class="switch-btn" :class="{open: switchStatus.minor}"></view>
</view>
</view>
<!-- 商品分类 -->
<view class="setting-item flex-row-between">
<view class="setting-label">商品分类</view>
<input
class="item-input"
type="text"
placeholder="请输入商品分类"
v-model="goodsInfo.category"
/>
</view>
<!-- 商品品牌 -->
<view class="setting-item flex-row-between">
<view class="setting-label">商品品牌</view>
<input
class="item-input"
type="text"
placeholder="请输入商品品牌"
v-model="goodsInfo.brand"
/>
</view>
<!-- 商品编码 -->
<view class="setting-item flex-row-between">
<view class="setting-label">商品编码</view>
<input
class="item-input"
type="text"
placeholder="请输入商品编码"
v-model="goodsInfo.code"
/>
</view>
</view>
<!-- 底部固定保存按钮 -->
<view class="bottom-submit">
<button class="save-btn" @click="saveGoods"></button>
</view>
</view>
</template>
<script>
import { getProductDetail, updateProduct, updateProductWithFile } from '@/api/product'
import { getStoreId } from '@/utils/auth'
export default {
name: 'GoodsEdit',
data() {
return {
productId: null,
goodsInfo: {
imageUrl: 'https://p3-flow-imagex-sign.byteimg.com/tos-cn-i-a9rns2rl98/1b5ce0f17f5e4942a350d4c58f83dde9.png~tplv-a9rns2rl98-image.png?lk3s=8e244e95&rcl=202601151851433FA5618C22F61C91356F&rrcfp=dafada99&x-expires=2084698303&x-signature=mApXMrIjzYh%2B1B6IE2pHDPy9Z2U%3D',
name: '农夫山泉饮用天然水 550ML',
unit: '瓶',
barcode: '6921168509256',
salePrice: 5.5,
stock: 1,
buyPrice: 1.0,
category: '默认',
brand: '默认品牌',
code: '1',
shelfCode: '',
productionDate: ''
},
priceOptions: [
{ price: 2.0, desc: '92%商家卖' },
{ price: 1.5, desc: '2%商家卖' },
{ price: 1.8, desc: '1%商家卖' }
],
activePrice: 2.0,
switchStatus: {
expire: true,
minor: false
},
expireSettings: {
days: 240,
warnDays: 24,
activeOption: { label: '8个月', days: 240 }
},
expireOptionList: [
{ label: '1年', days: 365 },
{ label: '9个月', days: 270 },
{ label: '6个月', days: 180 },
{ label: '8个月', days: 240 },
{ label: '10个月', days: 300 }
]
}
},
onLoad(options) {
console.log('编辑页面参数:', options);
if (options.id) {
this.productId = options.id;
} else {
uni.showToast({
title: '缺少商品ID参数',
icon: 'none'
});
}
},
onShow() {
if (this.productId) {
this.loadProductDetail();
}
},
computed: {
warningDaysDisplay() {
if (!this.expireSettings.days) return '';
const days = parseInt(this.expireSettings.days);
return days.toString().substring(0, 2);
}
},
methods: {
navBack() {
uni.navigateBack({ delta: 1 });
},
previewImage() {
uni.previewImage({
urls: [this.goodsInfo.imageUrl],
current: this.goodsInfo.imageUrl
});
},
chooseImage() {
uni.chooseImage({
count: 1,
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera'],
success: (res) => {
this.goodsInfo.imageUrl = res.tempFilePaths[0];
}
});
},
scanBarcode() {
uni.scanCode({
onlyFromCamera: true,
scanType: ['barCode'],
success: (res) => {
this.goodsInfo.barcode = res.result;
uni.showToast({ title: '扫码成功', icon: 'success' });
},
fail: () => {
uni.showToast({ title: '扫码取消', icon: 'none' });
}
});
},
setSalePrice(price) {
this.activePrice = price;
this.goodsInfo.salePrice = price;
},
toggleSwitch(type) {
if (type === 'expire') {
this.switchStatus.expire = !this.switchStatus.expire;
} else if (type === 'minor') {
this.switchStatus.minor = !this.switchStatus.minor;
}
},
toggleExpirationManagement(e) {
this.switchStatus.expire = e.detail.value;
},
setExpirationDays(days) {
this.expireSettings.days = days;
const warningDays = days.toString().substring(0, 2);
this.expireSettings.warnDays = warningDays;
uni.showToast({ title: `已设置为${days}`, icon: 'success', duration: 1500 });
},
async loadProductDetail() {
if (!this.productId) {
uni.showToast({
title: '缺少商品ID',
icon: 'none'
});
return;
}
try {
console.log('正在获取商品详情ID:', this.productId);
const res = await getProductDetail(this.productId);
console.log('商品详情接口返回:', res);
if (res && res.code === 200 && res.data) {
const data = res.data;
this.goodsInfo = {
imageUrl: data.mainImage ? 'http://193.112.94.36:8081' + data.mainImage : this.goodsInfo.imageUrl,
name: data.productName || this.goodsInfo.name,
unit: this.goodsInfo.unit,
barcode: data.productBarCode || this.goodsInfo.barcode,
salePrice: data.storePrice || this.goodsInfo.salePrice,
stock: data.stockQuantity || this.goodsInfo.stock,
buyPrice: data.costPrice || this.goodsInfo.buyPrice,
category: this.goodsInfo.category,
brand: this.goodsInfo.brand,
code: data.productCode || this.goodsInfo.code
};
this.activePrice = data.storePrice || this.goodsInfo.salePrice;
if (data.shelfLife) {
this.switchStatus.expire = true;
this.expireSettings.days = data.shelfLife;
this.expireSettings.warnDays = data.approaching || 24;
const days = data.shelfLife;
const option = this.expireOptionList.find(item => item.days === days);
if (option) {
this.expireSettings.activeOption = option;
}
} else {
this.switchStatus.expire = false;
}
console.log('商品详情数据:', this.goodsInfo);
} else {
uni.showToast({
title: res?.msg || '获取商品详情失败',
icon: 'none'
});
}
} catch (error) {
console.error('获取商品详情失败:', error);
uni.showToast({
title: '网络请求失败',
icon: 'none'
});
}
},
setExpireDays(item) {
this.expireSettings.days = item.days;
this.expireSettings.activeOption = item;
},
addUnit() {
uni.showModal({
title: '提示',
content: '是否添加商品多单位规格?',
success: (res) => {
if (res.confirm) uni.showToast({ title: '添加成功', icon: 'success' });
}
});
},
async saveGoods() {
if (!this.goodsInfo.name || !this.goodsInfo.name.trim()) {
uni.showToast({ title: '请输入商品名称', icon: 'none' });
return;
}
if (!this.goodsInfo.salePrice) {
uni.showToast({ title: '请输入售价', icon: 'none' });
return;
}
if (this.switchStatus.expire && !this.expireSettings.days) {
uni.showToast({ title: '请选择保质期天数', icon: 'none' });
return;
}
const storeId = getStoreId();
if (!storeId) {
uni.showToast({ title: '请先选择门店', icon: 'none' });
return;
}
uni.showLoading({ title: '保存中...' });
try {
const formData = {
id: this.productId,
productName: this.goodsInfo.name,
productBarCode: this.goodsInfo.barcode,
storePrice: this.goodsInfo.salePrice,
stockQuantity: this.goodsInfo.stock,
costPrice: this.goodsInfo.buyPrice,
productCode: this.goodsInfo.code,
shelfCode: this.goodsInfo.shelfCode || '',
expirationManagement: this.switchStatus.expire ? 1 : 0,
shelfLife: this.switchStatus.expire ? this.expireSettings.days : null,
approaching: this.switchStatus.expire ? this.expireSettings.warnDays : null,
productionDate: this.goodsInfo.productionDate || '',
storeId: storeId
};
const res = await updateProductWithFile(this.goodsInfo.imageUrl || '', formData);
uni.hideLoading();
if (res.code === 200) {
uni.showToast({
title: '保存成功',
icon: 'success',
duration: 2000
});
setTimeout(() => {
this.navBack();
}, 2000);
} else {
uni.showToast({
title: res.msg || '保存失败',
icon: 'none'
});
}
} catch (error) {
uni.hideLoading();
console.error('保存商品失败:', error);
uni.showToast({
title: '网络请求失败',
icon: 'none'
});
}
}
}
};
</script>
<style lang="scss" scoped>
// 全局样式
.goods-edit-page {
background-color: #f5f5f5;
min-height: 100vh;
box-sizing: border-box;
padding-bottom: 120rpx;
}
.flex-row {
display: flex;
align-items: center;
}
.flex-row-between {
display: flex;
align-items: center;
justify-content: space-between;
}
// 顶部导航栏
.navbar {
height: 88rpx;
background: #E62429;
color: #FFFFFF;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 30rpx;
position: sticky;
top: 0;
left: 0;
right: 0;
z-index: 99;
.nav-left, .nav-right {
width: 60rpx;
text-align: center;
.icon {
font-size: 32rpx;
}
}
.nav-title {
font-size: 36rpx;
font-weight: 500;
}
}
// 商品图片区域
.goods-img-box {
background: #FFFFFF;
padding: 40rpx 0;
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 20rpx;
.goods-img {
width: 220rpx;
height: 220rpx;
border-radius: 8rpx;
margin-bottom: 30rpx;
}
.change-img-btn {
font-size: 28rpx;
color: #666666;
padding: 12rpx 30rpx;
border: 1px solid #E5E5E5;
border-radius: 6rpx;
}
}
// 表单容器
.form-container {
background: #FFFFFF;
margin-bottom: 20rpx;
.form-item {
padding: 30rpx;
border-bottom: 1px solid #F0F0F0;
display: flex;
justify-content: space-between;
align-items: center;
.form-label {
font-size: 30rpx;
color: #333333;
.required {
color: #E62429;
margin-right: 4rpx;
}
}
.form-value {
font-size: 30rpx;
color: #333333;
}
.scan-btn {
margin-left: 20rpx;
padding: 8rpx 20rpx;
background: #F5F5F5;
border-radius: 4rpx;
font-size: 26rpx;
color: #666666;
}
}
// 推荐价格区域(修正后的排版)
.price-recommend-section {
padding: 0 30rpx 30rpx;
.price-tips {
width: 100%;
font-size: 24rpx;
color: #999999;
margin-bottom: 15rpx;
.tips-icon {
display: inline-block;
width: 20rpx;
height: 20rpx;
border: 1px solid #999;
border-radius: 50%;
text-align: center;
line-height: 20rpx;
font-size: 18rpx;
margin-left: 5rpx;
}
}
.price-option-list {
width: 100%;
display: flex;
gap: 20rpx;
.price-option {
flex: 1;
background: #F5F5F5;
border-radius: 8rpx;
padding: 20rpx 10rpx;
text-align: center;
.option-text {
font-size: 24rpx;
color: #666;
}
.option-price {
display: block;
font-size: 32rpx;
color: #E62429;
font-weight: bold;
margin-top: 8rpx;
}
&.active {
background: #E62429;
.option-text, .option-price {
color: #FFFFFF;
}
}
}
}
}
.form-placeholder {
font-size: 28rpx;
color: #CCCCCC;
}
.right-arrow {
font-size: 28rpx;
color: #CCCCCC;
margin-left: 10rpx;
}
.add-unit-btn {
margin: 30rpx;
padding: 20rpx;
text-align: center;
font-size: 28rpx;
color: #666666;
border: 1px dashed #CCCCCC;
border-radius: 8rpx;
background: #FFFFFF;
}
}
// 其他设置区域
.settings-container {
background: #FFFFFF;
.settings-title {
font-size: 32rpx;
font-weight: bold;
color: #333333;
padding: 30rpx;
border-bottom: 1px solid #F0F0F0;
}
.setting-item {
padding: 30rpx;
border-bottom: 1px solid #F0F0F0;
.setting-label {
font-size: 30rpx;
color: #333333;
.tips-icon {
display: inline-block;
width: 20rpx;
height: 20rpx;
border: 1px solid #999;
border-radius: 50%;
text-align: center;
line-height: 20rpx;
font-size: 18rpx;
margin-left: 5rpx;
}
}
.setting-left {
.setting-tips {
font-size: 24rpx;
color: #1677FF;
margin-top: 8rpx;
}
}
.setting-value {
font-size: 30rpx;
color: #333333;
}
.switch-box {
width: 70rpx;
height: 40rpx;
background: #E5E5E5;
border-radius: 20rpx;
position: relative;
display: flex;
align-items: center;
padding: 0 4rpx;
.switch-bg {
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
border-radius: 20rpx;
background: #E5E5E5;
transition: all 0.3s ease;
&.open {
background: #E62429;
}
}
.switch-btn {
width: 32rpx;
height: 32rpx;
background: #FFFFFF;
border-radius: 50%;
position: relative;
z-index: 2;
transition: all 0.3s ease;
&.open {
transform: translateX(30rpx);
}
}
}
}
.expire-options {
display: flex;
padding: 0 30rpx 30rpx;
gap: 15rpx;
overflow-x: auto;
.expire-option {
flex-shrink: 0;
background: #F5F5F5;
border: 2px solid transparent;
border-radius: 8rpx;
padding: 15rpx 20rpx;
text-align: center;
position: relative;
.option-text {
font-size: 28rpx;
color: #333;
}
.option-days {
display: block;
font-size: 24rpx;
color: #E62429;
margin-top: 5rpx;
}
&.active {
border-color: #E62429;
background: #FFFFFF;
.checked-icon {
display: block;
position: absolute;
top: -10rpx;
right: -10rpx;
width: 30rpx;
height: 30rpx;
background: #E62429;
color: #FFFFFF;
border-radius: 50%;
font-size: 20rpx;
line-height: 30rpx;
text-align: center;
}
}
}
}
}
/* 保质期相关样式 */
.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:#333;margin-top:4rpx; }
/* 临期提醒天数样式 */
.warning-days-display { display:flex;align-items:center;flex:1;justify-content:flex-end; }
.warning-days-value { font-size:36rpx;color:#333;font-weight:600;margin-right:8rpx; }
.warning-days-unit { font-size:28rpx;color:#333; }
/* 表单项样式 */
.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-input { flex: 1; }
.price-input { flex: 1; }
.price-unit { font-size: 32rpx;color: #333;margin-left: 10rpx; }
.unit-text { font-size: 32rpx;color: #333;margin-left: 10rpx; }
// 底部保存按钮
.bottom-submit {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: #FFFFFF;
padding: 20rpx 30rpx;
border-top: 1px solid #F0F0F0;
z-index: 90;
.save-btn {
width: 100%;
height: 90rpx;
line-height: 90rpx;
background: #E62429;
color: #FFFFFF;
font-size: 32rpx;
font-weight: 500;
border-radius: 8rpx;
padding: 0;
margin: 0;
&::after {
border: none;
}
}
}
</style>