ry_app/pages/edit/edit.vue

892 lines
24 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<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>