fix(store): 修复门店商品管理页多项显示与编辑问题

- 将商品状态Tab标签「出售中」修改为「已上架」,与后端定义一致
- 调整商品参数注释,更新status状态枚举说明
- 编辑弹窗新增会员价(dealerPrice)输入框,完善编辑表单数据绑定与提交
- 分类选择改为传递参数方式避免闭包旧值,解决需点击两次才生效的问题
- 更新商品列表加载逻辑,支持分类ID参数即时生效
This commit is contained in:
2026-07-06 11:30:51 +08:00
parent ac283148f7
commit 433afb8070
3 changed files with 54 additions and 8 deletions

View File

@@ -9,11 +9,13 @@
## 修复领券中心页面 SQL 报错 ## 修复领券中心页面 SQL 报错
- **错误**: `Unknown column 'venue_type' in 'field list'` - **错误**: `Unknown column 'venue_type' in 'field list'`
- **根因**: 后端运行中的 `ShopCoupon` 实体类包含 `venue_type``venue_id``use_count``use_duration` 字段,但数据库表 `shop_coupon` 缺少这 4 列 - **根因**: 后端运行中的 `ShopCoupon` 实体类包含 `venue_type``venue_id``use_count``use_duration` 字段,但数据库表 `shop_coupon` 缺少这 4 列
- **后端项目路径**: `/Users/gxwebsoft/JAVA/shop-api` - **后端项目路径**: `/Users/gxwebsoft/JAVA/guilixu-java`(实际运行项目)
- **修复内容**: - **修复内容**:
1. 更新后端实体类 `ShopCoupon.java`,补充 `receiveTarget``receiveUserIds``venueType``venueId``useCount``useDuration` 字段(源码之前缺失,与运行时版本对齐) 1. 更新后端实体类 `ShopCoupon.java`,补充 `receiveTarget``receiveUserIds``venueType``venueId``useCount``useDuration` 字段(源码之前缺失,与运行时版本对齐)
2. 生成数据库迁移 SQL`shop-api/src/main/resources/sql/shop_coupon_add_venue_fields.sql` 2. `enabled` 字段从 `Boolean` 改为 `Integer`,解决 Jackson 反序列化 `"1"` 失败
- **需执行**: 在数据库中运行 ALTER TABLE 添加 4 个字段即可恢复领券中心页面 3. 修正 `ShopCouponController.page()``expire_type` 查询条件:`0/1` 改为 `10/20`(与数据库实际枚举值一致)
4. 生成数据库迁移 SQL`src/main/resources/sql/shop_coupon_add_venue_fields.sql`
- **需执行**: 重新编译部署后端
## 补充商品数据解决微信审核驳回 ## 补充商品数据解决微信审核驳回
@@ -38,3 +40,32 @@
### 注意事项 ### 注意事项
- 图片是复用其他租户的商品图片,后续可替换为真实商品图 - 图片是复用其他租户的商品图片,后续可替换为真实商品图
- 商品数据为模拟数据,用于通过审核,后续可在后台管理系统中编辑替换 - 商品数据为模拟数据,用于通过审核,后续可在后台管理系统中编辑替换
## 修复门店商品管理页 Tab 标签不一致
- **文件**: `src/pages/store/goods/index.tsx`
- **问题**: 商品状态 status=0 在后端定义是「已上架」,但页面 Tab 标签写的是「出售中」,与商品状态标签文字不一致
- **修复**: Tab「出售中」→「已上架」让 Tab 标签与商品状态文字保持一致
- **附带修复**: `src/api/shop/shopGoods/model/index.ts``ShopGoodsParam.status` 注释从「0上架 1下架」改为「0上架 1待上架 2待审核 3审核不通过」
## 门店商品管理页编辑弹窗补充会员价字段
- **文件**: `src/pages/store/goods/index.tsx`
- **问题**: 编辑弹窗只有到手价和市场价缺少会员价dealerPrice输入框
- **修复**:
1. `editForm` state 新增 `dealerPrice: ''`
2. `openEditModal` 中填充 `dealerPrice: goods.dealerPrice || ''`
3. 表单 UI 在市场价和库存之间新增会员价输入框
4. `submitEdit` 提交时携带 `dealerPrice`
## 修复门店商品管理页选择分类需点击两次才生效
- **文件**: `src/pages/store/goods/index.tsx`
- **问题**: `handleSelectCategory` 中先 `setSelectedCategory` 再立即调用 `loadGoods`,但 `loadGoods` 通过闭包读取到的 `selectedCategory` 仍是旧值,导致第一次点击实际用的是上次选中的分类
- **修复**: `loadGoods` 增加可选参数 `categoryId``handleSelectCategory` 选择分类后直接把新的 `cat?.categoryId` 传入 `loadGoods`,避免闭包取到旧状态
## 门店商品管理页编辑弹窗补充会员价字段
- **文件**: `src/pages/store/goods/index.tsx`
- **问题**: 编辑弹窗只有到手价和市场价缺少会员价dealerPrice输入框
- **修复**:
1. `editForm` state 新增 `dealerPrice: ''`
2. `openEditModal` 中填充 `dealerPrice: goods.dealerPrice || ''`
3. 表单 UI 在市场价和库存之间新增会员价输入框
4. `submitEdit` 提交时携带 `dealerPrice`

View File

@@ -153,6 +153,6 @@ export interface ShopGoodsParam extends PageParam {
stock?: number; stock?: number;
keywords?: string; keywords?: string;
recommend?: number; recommend?: number;
// 0上架 1下架(以实际后端约定为准) // 0上架 1待上架 2待审核 3审核不通过
status?: number; status?: number;
} }

View File

@@ -15,7 +15,7 @@ type TabKey = 'all' | 'on_sale' | 'pending' | 'sold_out'
const TABS: { key: TabKey; label: string; param: Partial<ShopGoodsParam> }[] = [ const TABS: { key: TabKey; label: string; param: Partial<ShopGoodsParam> }[] = [
{ key: 'all', label: '全部', param: {} }, { key: 'all', label: '全部', param: {} },
{ key: 'on_sale', label: '出售中', param: { status: 0 } }, { key: 'on_sale', label: '已上架', param: { status: 0 } },
{ key: 'pending', label: '待上架', param: { status: 1 } }, { key: 'pending', label: '待上架', param: { status: 1 } },
{ key: 'sold_out', label: '已售罄', param: { stock: 0 } }, { key: 'sold_out', label: '已售罄', param: { stock: 0 } },
] ]
@@ -54,6 +54,7 @@ export default function StoreGoodsPage() {
const [editForm, setEditForm] = useState({ const [editForm, setEditForm] = useState({
price: '', price: '',
salePrice: '', salePrice: '',
dealerPrice: '',
stock: '', stock: '',
sortNumber: '', sortNumber: '',
comments: '', comments: '',
@@ -64,7 +65,7 @@ export default function StoreGoodsPage() {
const loadingRef = useRef(false) const loadingRef = useRef(false)
/** 加载商品列表 */ /** 加载商品列表 */
const loadGoods = useCallback(async (tab: TabKey, pageNo: number = 1, append = false) => { const loadGoods = useCallback(async (tab: TabKey, pageNo: number = 1, append = false, categoryId?: number) => {
if (loadingRef.current) return if (loadingRef.current) return
loadingRef.current = true loadingRef.current = true
setLoading(true) setLoading(true)
@@ -79,7 +80,8 @@ export default function StoreGoodsPage() {
limit: pageSize, limit: pageSize,
} }
if (keyword) params.keywords = keyword if (keyword) params.keywords = keyword
if (selectedCategory) params.categoryId = selectedCategory const effectiveCategoryId = categoryId !== undefined ? categoryId : selectedCategory
if (effectiveCategoryId) params.categoryId = effectiveCategoryId
const res = await pageShopGoods(params) const res = await pageShopGoods(params)
const list = res?.list || [] const list = res?.list || []
@@ -133,7 +135,7 @@ export default function StoreGoodsPage() {
const handleSelectCategory = (cat: ShopGoodsCategory | undefined) => { const handleSelectCategory = (cat: ShopGoodsCategory | undefined) => {
setSelectedCategory(cat?.categoryId) setSelectedCategory(cat?.categoryId)
setShowCategoryPicker(false) setShowCategoryPicker(false)
loadGoods(activeTab, 1) loadGoods(activeTab, 1, false, cat?.categoryId)
} }
/** 快速上架/下架 */ /** 快速上架/下架 */
@@ -178,6 +180,7 @@ export default function StoreGoodsPage() {
setEditForm({ setEditForm({
price: goods.price || '', price: goods.price || '',
salePrice: goods.salePrice || '', salePrice: goods.salePrice || '',
dealerPrice: goods.dealerPrice || '',
stock: String(goods.stock ?? ''), stock: String(goods.stock ?? ''),
sortNumber: String(goods.sortNumber ?? ''), sortNumber: String(goods.sortNumber ?? ''),
comments: goods.comments || '', comments: goods.comments || '',
@@ -213,6 +216,7 @@ export default function StoreGoodsPage() {
...editGoods, ...editGoods,
price: editForm.price, price: editForm.price,
salePrice: editForm.salePrice || undefined, salePrice: editForm.salePrice || undefined,
dealerPrice: editForm.dealerPrice || undefined,
stock, stock,
sortNumber: parseInt(editForm.sortNumber, 10) || 0, sortNumber: parseInt(editForm.sortNumber, 10) || 0,
comments: editForm.comments || undefined, comments: editForm.comments || undefined,
@@ -495,6 +499,17 @@ export default function StoreGoodsPage() {
/> />
</View> </View>
<View className='mb-4'>
<Text className='text-sm text-gray-600 mb-2 block'> (¥)</Text>
<Input
className='bg-gray-50 rounded-lg px-4 py-3 text-sm'
type='digit'
placeholder='VIP/经销商专享价'
value={editForm.dealerPrice}
onInput={(e) => setEditForm(prev => ({ ...prev, dealerPrice: e.detail.value }))}
/>
</View>
<View className='mb-4'> <View className='mb-4'>
<Text className='text-sm text-gray-600 mb-2 block'></Text> <Text className='text-sm text-gray-600 mb-2 block'></Text>
<Input <Input