ry_app/package_a/edit/edit.vue

1486 lines
41 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">
2026-02-04 14:57:28 +08:00
<!-- <text class="icon"></text> -->
2026-01-19 16:52:24 +08:00
</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>
2026-01-23 15:36:51 +08:00
<!-- 商品分类点击触发弹窗 -->
<view class="setting-item flex-row-between" @click="showCategorySelector = true; getCategoryData()">
2026-01-19 16:52:24 +08:00
<view class="setting-label">商品分类</view>
2026-01-23 15:36:51 +08:00
<view class="flex-row">
<text class="selected-value">{{ goodsInfo.category || '请选择分类' }}</text>
<text class="right-arrow">></text>
</view>
2026-01-19 16:52:24 +08:00
</view>
2026-01-22 21:27:11 +08:00
<!-- 商品品牌点击触发弹窗 -->
<view class="setting-item flex-row-between" @click="showBrandSelector = true; getBrandData()">
2026-01-19 16:52:24 +08:00
<view class="setting-label">商品品牌</view>
2026-01-22 21:27:11 +08:00
<view class="flex-row">
<text class="selected-value">{{ goodsInfo.brand || '请选择品牌' }}</text>
<text class="right-arrow">></text>
</view>
</view>
2026-01-23 15:36:51 +08:00
<!-- 品牌选择弹窗 -->
2026-01-22 21:27:11 +08:00
<view v-if="showBrandSelector" class="brand-selector-overlay" @click="closeBrandSelector">
<view class="brand-selector-container" @click.stop>
<!-- 头部 -->
<view class="selector-header">
<view class="header-left" @click="closeBrandSelector">
<text class="close-icon">×</text>
</view>
<view class="header-title">选择品牌</view>
<view class="header-right" @click="confirmSelectedBrand">
<text class="confirm-btn">完成</text>
</view>
</view>
<!-- 搜索框 -->
2026-01-26 18:58:10 +08:00
<view class="search-box" style="z-index: 10000; pointer-events: auto; display: flex; align-items: center; gap: 10rpx;">
2026-01-22 21:27:11 +08:00
<input
type="text"
2026-01-26 18:58:10 +08:00
placeholder="搜索品牌名称1"
:value="brandSearchKeyword"
@input="brandSearchKeyword = $event.detail.value"
style="
flex: 1;
padding: 20rpx;
background: #f5f5f5;
border-radius: 8rpx;
font-size: 28rpx;
border: 1rpx solid #ddd;
z-index: 10001;
pointer-events: auto;
user-select: text;
-webkit-user-select: text;
"
2026-01-22 21:27:11 +08:00
/>
2026-01-26 18:58:10 +08:00
<view
@click="performBrandSearch"
style="
padding: 15rpx 25rpx;
background-color: #E62429;
color: white;
border-radius: 8rpx;
font-size: 28rpx;
z-index: 10002;
pointer-events: auto;
"
>
搜索
</view>
2026-01-22 21:27:11 +08:00
</view>
<!-- 品牌列表二级树状结构 -->
<view class="brand-list">
<!-- 普通品牌项 - 默认品牌 -->
<view
class="brand-item"
:class="{active: selectedBrand === '默认品牌'}"
@click="selectBrand('默认品牌')"
>
默认品牌
</view>
<!-- 动态生成品牌列表 -->
<template v-for="brand in brandData">
<!-- 父节点 -->
<view
class="brand-parent-item"
@click="toggleBrandExpand(brand.id)"
:key="brand.id"
>
<text
class="expand-icon"
:class="{expanded: expandedBrands[brand.id]}"
></text>
<text class="parent-text">{{ brand.brandName }}</text>
</view>
<!-- 子节点展开时显示 -->
<view v-if="expandedBrands[brand.id]" class="brand-child-item" :key="brand.id + '-children'">
<view
v-for="child in brand.children"
:key="child.id"
class="brand-item child"
:class="{active: selectedBrand === child.brandName}"
@click="selectBrand(child.brandName)"
>
{{ child.brandName }}
</view>
</view>
</template>
</view>
<!-- 品牌管理入口 -->
<view class="brand-management" @click="goToBrandManagement">
<text class="management-text">品牌管理</text>
</view>
</view>
2026-01-19 16:52:24 +08:00
</view>
2026-01-23 15:36:51 +08:00
<!-- 商品分类选择弹窗 -->
<view v-if="showCategorySelector" class="category-selector-overlay" @click="closeCategorySelector">
<view class="category-selector-container" @click.stop>
<!-- 头部 -->
<view class="selector-header">
<view class="header-left" @click="closeCategorySelector">
<text class="close-icon">×</text>
</view>
<view class="header-title">选择分类</view>
<view class="header-right" @click="confirmSelectedCategory">
<text class="confirm-btn">完成</text>
</view>
</view>
<!-- 搜索框 -->
2026-01-26 18:58:10 +08:00
<view class="search-box" style="z-index: 10000; pointer-events: auto; display: flex; align-items: center; gap: 10rpx;">
2026-01-23 15:36:51 +08:00
<input
type="text"
placeholder="搜索分类名称"
2026-01-26 18:58:10 +08:00
:value="categorySearchKeyword"
@input="categorySearchKeyword = $event.detail.value"
style="
flex: 1;
padding: 20rpx;
background: #f5f5f5;
border-radius: 8rpx;
font-size: 28rpx;
border: 1rpx solid #ddd;
z-index: 10001;
pointer-events: auto;
user-select: text;
-webkit-user-select: text;
"
2026-01-23 15:36:51 +08:00
/>
2026-01-26 18:58:10 +08:00
<view
@click="performCategorySearch"
style="
padding: 15rpx 25rpx;
background-color: #E62429;
color: white;
border-radius: 8rpx;
font-size: 28rpx;
z-index: 10002;
pointer-events: auto;
"
>
搜索
</view>
2026-01-23 15:36:51 +08:00
</view>
<!-- 分类列表二级树状结构 -->
<view class="category-list">
<!-- 动态生成分类列表 -->
<template v-for="category in categoryData">
<!-- 父节点 -->
<view
class="category-parent-item"
@click="toggleCategoryExpand(category.id)"
:key="category.id"
>
<text
class="expand-icon"
:class="{expanded: expandedCategories[category.id]}"
></text>
<text class="parent-text">{{ category.classificationName }}</text>
</view>
<!-- 子节点展开时显示 -->
<view v-if="expandedCategories[category.id]" class="category-child-item" :key="category.id + '-children'">
<view
v-for="child in category.children"
:key="child.id"
class="category-item child"
:class="{active: selectedCategory === child.classificationName}"
@click="selectCategory(child.classificationName)"
>
{{ child.classificationName }}
</view>
</view>
</template>
</view>
<!-- 分类管理入口 -->
<view class="category-management" @click="goToCategoryManagement">
<text class="management-text">分类管理</text>
</view>
</view>
</view>
2026-01-19 16:52:24 +08:00
<!-- 商品编码 -->
<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>
2026-01-26 18:58:10 +08:00
import { getProductDetail, updateProduct, updateProductWithFile, getClassificationTree, getBrandTree } from '@/api/product'
2026-01-19 16:52:24 +08:00
import { getStoreId } from '@/utils/auth'
import apiUrl from '@/utils/api'
2026-01-19 16:52:24 +08:00
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: '默认',
2026-01-23 15:36:51 +08:00
brand: '那',
2026-01-19 16:52:24 +08:00
code: '1',
shelfCode: '',
2026-02-04 20:59:33 +08:00
productionDate: '',
2026-01-19 16:52:24 +08:00
},
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 }
2026-01-22 21:27:11 +08:00
],
// 品牌选择弹窗相关
showBrandSelector: false,
brandSearchKeyword: '',
2026-01-23 15:36:51 +08:00
selectedBrand: '那',
brandData: [],
expandedBrands: {},
// 分类选择弹窗相关
showCategorySelector: false,
categorySearchKeyword: '',
selectedCategory: 'default',
categoryData: [], // 分类数据
expandedCategories: {} // 记录展开状态的对象
2026-01-19 16:52:24 +08:00
}
},
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 ? apiUrl + data.mainImage : this.goodsInfo.imageUrl,
2026-01-19 16:52:24 +08:00
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,
2026-01-23 15:36:51 +08:00
category: data.classifcation || this.goodsInfo.category,
2026-01-22 21:27:11 +08:00
brand: data.productBrand || this.goodsInfo.brand,
2026-02-04 20:59:33 +08:00
code: data.productCode || this.goodsInfo.code,
shelfCode:data.shelfCode || this.goodsInfo.shelfCode,
2026-01-19 16:52:24 +08:00
};
this.activePrice = data.storePrice || this.goodsInfo.salePrice;
2026-01-22 21:27:11 +08:00
this.selectedBrand = data.productBrand || this.goodsInfo.brand;
2026-01-23 15:36:51 +08:00
this.selectedCategory = data.classifcation || this.goodsInfo.category;
2026-01-19 16:52:24 +08:00
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,
2026-02-04 20:59:33 +08:00
shelfLife: this.switchStatus.expire ? this.expireSettings.days : '',
approaching: this.switchStatus.expire ? this.expireSettings.warnDays : '',
2026-01-19 16:52:24 +08:00
productionDate: this.goodsInfo.productionDate || '',
2026-01-22 21:27:11 +08:00
storeId: storeId,
2026-01-23 15:36:51 +08:00
productBrand: this.goodsInfo.brand,
classification: this.goodsInfo.category
2026-01-19 16:52:24 +08:00
};
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'
});
}
2026-01-22 21:27:11 +08:00
},
2026-01-26 18:58:10 +08:00
// 获取品牌数据 - 简化版,只获取所有品牌
2026-01-22 21:27:11 +08:00
async getBrandData() {
try {
2026-01-26 18:58:10 +08:00
// 使用默认门店ID 2如果未找到门店ID
const storeId = getStoreId() || 2;
// 直接获取所有品牌
const res = await getBrandTree(storeId);
2026-01-22 21:27:11 +08:00
2026-01-26 18:58:10 +08:00
if (res.code === 200) {
this.brandData = res.data || [];
2026-01-22 21:27:11 +08:00
} else {
2026-01-26 18:58:10 +08:00
console.log('获取品牌数据失败:', res);
2026-01-22 21:27:11 +08:00
}
} catch (error) {
console.error('获取品牌数据失败:', error);
}
},
2026-01-26 18:58:10 +08:00
// 获取分类数据 - 简化版,只获取所有分类
2026-01-23 15:36:51 +08:00
async getCategoryData() {
try {
2026-01-26 18:58:10 +08:00
// 使用默认门店ID 2如果未找到门店ID
const storeId = getStoreId() || 2;
2026-01-23 15:36:51 +08:00
2026-01-26 18:58:10 +08:00
// 直接获取所有分类
const res = await getClassificationTree(storeId);
2026-01-23 15:36:51 +08:00
2026-01-26 18:58:10 +08:00
if (res.code === 200) {
this.categoryData = res.data || [];
2026-01-23 15:36:51 +08:00
} else {
2026-01-26 18:58:10 +08:00
console.log('获取分类数据失败:', res);
2026-01-23 15:36:51 +08:00
}
} catch (error) {
console.error('获取分类数据失败:', error);
2026-01-26 18:58:10 +08:00
}
},
// 执行分类搜索
async performCategorySearch() {
try {
// 使用默认门店ID 2如果未找到门店ID
const storeId = getStoreId() || 2;
// 调用分类搜索API
const res = await getClassificationTree(storeId, this.categorySearchKeyword);
if (res.code === 200) {
this.categoryData = res.data || [];
} else {
console.log('搜索分类失败:', res);
}
} catch (error) {
console.error('搜索分类失败:', error);
}
},
// 分类输入处理 - 仅更新数据不触发API
handleCategoryInput(e) {
// 直接更新数据让v-model正常工作
this.categorySearchKeyword = e.detail.value;
},
// 品牌输入处理 - 仅更新数据不触发API
handleBrandInput(e) {
// 直接更新数据让v-model正常工作
this.brandSearchKeyword = e.detail.value;
},
// 执行品牌搜索
async performBrandSearch() {
try {
// 使用默认门店ID 2如果未找到门店ID
const storeId = getStoreId() || 2;
// 调用品牌搜索API
const res = await getBrandTree(storeId, this.brandSearchKeyword);
if (res.code === 200) {
this.brandData = res.data || [];
} else {
console.log('搜索品牌失败:', res);
}
} catch (error) {
console.error('搜索品牌失败:', error);
2026-01-23 15:36:51 +08:00
}
},
2026-01-22 21:27:11 +08:00
// 品牌选择弹窗方法
selectBrand(brandName) {
this.selectedBrand = brandName;
},
confirmSelectedBrand() {
this.goodsInfo.brand = this.selectedBrand;
this.showBrandSelector = false;
this.brandSearchKeyword = '';
},
closeBrandSelector() {
this.showBrandSelector = false;
this.brandSearchKeyword = '';
},
toggleBrandExpand(brandId) {
this.$set(this.expandedBrands, brandId, !this.expandedBrands[brandId]);
},
goToBrandManagement() {
this.showBrandSelector = false;
uni.navigateTo({
2026-03-28 21:37:27 +08:00
url: '/package_a/addBrand/addBrand'
2026-01-22 21:27:11 +08:00
});
2026-01-23 15:36:51 +08:00
},
// 分类选择弹窗方法
selectCategory(categoryName) {
this.selectedCategory = categoryName;
},
confirmSelectedCategory() {
// 根据选中值映射显示名称
const categoryMap = {
'rx': '处方药(RX)',
'vet-otc': '兽用非处方药',
'vet-rx': '兽用处方药',
'default': '默认',
'otc-child': 'OTC子分类示例',
'go-child': '子分类示例'
};
this.goodsInfo.category = categoryMap[this.selectedCategory] || this.selectedCategory;
this.showCategorySelector = false;
this.categorySearchKeyword = '';
},
closeCategorySelector() {
this.showCategorySelector = false;
this.categorySearchKeyword = '';
},
toggleCategoryExpand(key) {
this.$set(this.expandedCategories, key, !this.expandedCategories[key]);
},
goToCategoryManagement() {
this.showCategorySelector = false;
uni.navigateTo({
2026-03-28 21:37:27 +08:00
url: '/package_a/category/category'
2026-01-23 15:36:51 +08:00
});
2026-01-19 16:52:24 +08:00
}
}
};
</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;
}
}
}
2026-01-22 21:27:11 +08:00
2026-01-23 15:36:51 +08:00
// 商品品牌/分类选中值样式
2026-01-22 21:27:11 +08:00
.selected-value {
font-size: 30rpx;
color: #333;
margin-right: 10rpx;
}
// --------------------------
2026-01-23 15:36:51 +08:00
// 品牌选择弹窗样式
2026-01-22 21:27:11 +08:00
// --------------------------
.brand-selector-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: flex-end;
z-index: 999;
2026-01-26 18:58:10 +08:00
pointer-events: auto;
2026-01-22 21:27:11 +08:00
}
.brand-selector-container {
width: 100%;
background: #fff;
border-radius: 16rpx 16rpx 0 0;
max-height: 80vh;
display: flex;
flex-direction: column;
2026-01-26 18:58:10 +08:00
pointer-events: auto;
z-index: 1000;
2026-01-22 21:27:11 +08:00
}
.selector-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
.close-icon {
font-size: 36rpx;
color: #666;
}
.header-title {
font-size: 32rpx;
font-weight: 500;
color: #333;
}
.confirm-btn {
font-size: 30rpx;
color: #E62429;
}
}
.search-box {
padding: 20rpx 30rpx;
2026-01-26 18:58:10 +08:00
/* 确保搜索框容器可以被点击 */
pointer-events: auto;
z-index: 1000;
display: flex;
align-items: center;
gap: 10rpx;
2026-01-22 21:27:11 +08:00
.search-input {
2026-01-26 18:58:10 +08:00
flex: 1;
2026-01-22 21:27:11 +08:00
padding: 20rpx;
background: #f5f5f5;
border-radius: 8rpx;
font-size: 28rpx;
2026-01-26 18:58:10 +08:00
/* 确保输入框可以被点击和选中 */
pointer-events: auto;
z-index: 1001;
/* 确保输入框有焦点样式 */
border: 1rpx solid #ddd;
/* 确保可以选中文本 */
user-select: text;
/* 确保可以获得焦点 */
outline: none;
/* 确保触摸设备上可以正常使用 */
-webkit-user-select: text;
-webkit-tap-highlight-color: transparent;
&:focus {
border-color: #E62429;
box-shadow: 0 0 0 2rpx rgba(230, 36, 41, 0.1);
}
}
.search-button {
padding: 15rpx 25rpx;
background-color: #E62429;
color: white;
border-radius: 8rpx;
font-size: 28rpx;
/* 确保按钮可以被点击 */
pointer-events: auto;
z-index: 1002;
/* 添加点击反馈 */
&:active {
opacity: 0.8;
}
2026-01-22 21:27:11 +08:00
}
}
.brand-list {
flex: 1;
overflow-y: auto;
// 普通品牌项
.brand-item {
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
font-size: 30rpx;
color: #333;
position: relative;
&.active {
color: #E62429;
&::after {
content: "✓";
position: absolute;
right: 30rpx;
top: 50%;
transform: translateY(-50%);
font-size: 28rpx;
}
}
// 子节点样式
&.child {
padding-left: 60rpx;
background-color: #fafafa;
}
}
// 父节点样式
.brand-parent-item {
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
font-size: 30rpx;
color: #333;
display: flex;
align-items: center;
.expand-icon {
font-size: 24rpx;
margin-right: 10rpx;
color: #999;
transition: transform 0.2s ease;
&.expanded {
2026-01-23 15:36:51 +08:00
transform: rotate(90deg);
2026-01-22 21:27:11 +08:00
}
}
.parent-text {
flex: 1;
}
}
}
.brand-management {
padding: 30rpx;
text-align: center;
color: #1677FF;
font-size: 28rpx;
border-top: 1rpx solid #f0f0f0;
}
2026-01-23 15:36:51 +08:00
// --------------------------
// 分类选择弹窗样式(完全匹配图片)
// --------------------------
.category-selector-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: flex-end;
z-index: 999;
2026-01-26 18:58:10 +08:00
pointer-events: auto;
2026-01-23 15:36:51 +08:00
}
.category-selector-container {
width: 100%;
background: #fff;
border-radius: 16rpx 16rpx 0 0;
max-height: 80vh;
display: flex;
flex-direction: column;
2026-01-26 18:58:10 +08:00
pointer-events: auto;
z-index: 1000;
2026-01-23 15:36:51 +08:00
}
.category-list {
flex: 1;
overflow-y: auto;
// 普通分类项
.category-item {
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
font-size: 30rpx;
color: #333;
position: relative;
&.active {
border: 1rpx solid #E62429;
border-radius: 8rpx;
margin: 0 16rpx;
&::after {
content: "✓";
position: absolute;
right: 30rpx;
top: 50%;
transform: translateY(-50%);
font-size: 28rpx;
color: #E62429;
}
}
// 子节点样式
&.child {
padding-left: 60rpx;
background-color: #fafafa;
}
}
// 父节点样式
.category-parent-item {
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
font-size: 30rpx;
color: #333;
display: flex;
align-items: center;
.expand-icon {
font-size: 24rpx;
margin-right: 10rpx;
color: #999;
transition: transform 0.2s ease;
&.expanded {
transform: rotate(90deg);
}
}
.parent-text {
flex: 1;
}
}
}
.category-management {
padding: 30rpx;
text-align: center;
color: #1677FF;
font-size: 28rpx;
border-top: 1rpx solid #f0f0f0;
}
2026-01-19 16:52:24 +08:00
</style>