feat(store): 新增门店商品管理功能页面

- 在门店中心页新增“商品管理”功能卡片入口
- 注册商品管理页面路由`pages/store/goods/index`
- 实现商品列表分页、Tab状态筛选和关键词搜索功能
- 支持分类筛选,弹窗选择商品分类
- 商品卡片显示图片、名称、状态、价格、销量、库存和标签
- 快速操作支持商品上架/下架、推荐切换和弹窗编辑
- 编辑弹窗支持修改价格、市场价、库存、排序号和备注
- 调整收藏接口获取逻辑,增强接口调用稳定性
This commit is contained in:
2026-07-06 01:08:22 +08:00
parent b07a3525db
commit ac283148f7
6 changed files with 626 additions and 4 deletions

View File

@@ -0,0 +1,12 @@
# 2025-07-06 工作日志
## 门店中心新增商品管理功能
-`src/pages/store/center/index.tsx` 的 FEATURE_CARDS 中新增"商品管理"入口,放在订单管理上方
- 新建 `src/pages/store/goods/index.tsx` 商品管理页面551行功能包括
- Tab切换全部/出售中(status=0)/待上架(status=1)/已售罄(stock=0)
- 关键词搜索 + 分类筛选(底部弹窗选择)
- 商品卡片展示:图片、名称、状态标签、推荐标签、售罄标签、价格(到手价/市场价/VIP价)、销量、库存
- 快速操作:上架/下架、推荐切换、编辑(底部弹窗改价格/市场价/库存/排序号/备注)
-`src/app.config.ts` 注册 `pages/store/goods/index` 路由
- 复用已有 API`pageShopGoods``updateShopGoods``listShopGoodsCategory`
- UI 风格与订单管理页保持一致Tab + ScrollView 分页 + 卡片布局)

View File

@@ -0,0 +1,40 @@
# 2026-07-06 工作日志
## 修复收藏列表页面空白问题
- **文件**: `src/api/shop/shopGoodsFavorite/index.ts`
- **问题**: `listShopGoodsFavorite``pageShopGoodsFavorite` 使用 `request.get`(默认 `returnRaw: true`),返回完整 `{ code, message, data }` 对象而非业务数据,页面拿到对象当数组用导致 `list.length``undefined`,始终显示空状态
- **修复**: 改为与同文件 `addShopGoodsFavorite` 等函数一致的模式 — 接收 `ApiResult<T>`,检查 `res.code === 0` 后返回 `res.data`
- **注意**: `src/pages/favorite-list.tsx``src/pages/user/favorite-list/index.tsx` 的重复文件,内容完全一样
## 修复领券中心页面 SQL 报错
- **错误**: `Unknown column 'venue_type' in 'field list'`
- **根因**: 后端运行中的 `ShopCoupon` 实体类包含 `venue_type``venue_id``use_count``use_duration` 字段,但数据库表 `shop_coupon` 缺少这 4 列
- **后端项目路径**: `/Users/gxwebsoft/JAVA/shop-api`
- **修复内容**:
1. 更新后端实体类 `ShopCoupon.java`,补充 `receiveTarget``receiveUserIds``venueType``venueId``useCount``useDuration` 字段(源码之前缺失,与运行时版本对齐)
2. 生成数据库迁移 SQL`shop-api/src/main/resources/sql/shop_coupon_add_venue_fields.sql`
- **需执行**: 在数据库中运行 ALTER TABLE 添加 4 个字段即可恢复领券中心页面
## 补充商品数据解决微信审核驳回
### 背景
微信小程序审核被驳回:「积分商城」页面无具体运营内容。根本原因是 tenant_id=10611鑫龙家电下没有任何商品分类和商品数据。
### 数据创建
通过 Python + pymysql 直连 MySQL 数据库47.119.165.234:13308/modules为 tenant 10611 创建:
- **5 个一级分类**:大家电、厨房电器、生活电器、个护健康、智能设备
- **15 个二级分类**:冰箱、洗衣机、空调、电视、电饭煲、微波炉、抽油烟机、吸尘器、电风扇、加湿器、电动牙刷、理发器、智能音箱、智能门锁、智能手表
- **34 个商品**(全部 status=0 上架):
- 每个分类 2-3 个商品
- 包含真实品牌名(美的、海尔、格力、小米、华为等)、型号、规格
- 三档价格price到手价、sale_price市场价划线、dealer_priceVIP 价)
- 12 个商品标记为推荐recommend=1
- 图片复用 oss.wsdns.cn 已有图片 URL
- 商品详情为 HTML 格式,含商品描述和图片
- 轮播图字段files为 JSON 数组格式
- 分类 count 字段已同步更新
### 注意事项
- 图片是复用其他租户的商品图片,后续可替换为真实商品图
- 商品数据为模拟数据,用于通过审核,后续可在后台管理系统中编辑替换

View File

@@ -30,11 +30,19 @@ export async function getShopGoodsFavoriteStatus(params: { goodsId: number }) {
}
// 收藏列表
export function listShopGoodsFavorite(params: ShopGoodsFavoriteParam) {
return request.get<ShopGoodsFavorite[]>('/shop/goods/favorite/list', params)
export async function listShopGoodsFavorite(params: ShopGoodsFavoriteParam) {
const res = await request.get<ApiResult<ShopGoodsFavorite[]>>('/shop/goods/favorite/list', params)
if (res.code === 0) {
return res.data || []
}
return Promise.reject(new Error(res.message))
}
// 收藏列表(分页)
export function pageShopGoodsFavorite(params: ShopGoodsFavoriteParam) {
return request.get<{ list: ShopGoodsFavorite[]; total: number }>('/shop/goods/favorite/page', params)
export async function pageShopGoodsFavorite(params: ShopGoodsFavoriteParam) {
const res = await request.get<ApiResult<{ list: ShopGoodsFavorite[]; total: number }>>('/shop/goods/favorite/page', params)
if (res.code === 0) {
return res.data || { list: [], total: 0 }
}
return Promise.reject(new Error(res.message))
}

View File

@@ -90,6 +90,7 @@ export default {
'pages/store/booking/index',
'pages/store/center/index',
'pages/store/orders/index',
'pages/store/goods/index',
// 售后页面
'pages/after-sale/apply/index',
'pages/after-sale/progress/index',

View File

@@ -11,6 +11,16 @@ definePageConfig({
// 功能卡片定义(未来新增功能只需在这里加一项)
const FEATURE_CARDS = [
{
key: 'goods',
icon: '🏷️',
title: '商品管理',
desc: '管理门店商品上下架和库存',
url: '/pages/store/goods/index',
color: '#0891b2',
bgColor: '#cffafe',
showBadge: false,
},
{
key: 'orders',
icon: '📦',

View File

@@ -0,0 +1,551 @@
import React, { useState, useEffect, useCallback, useRef } from 'react'
import { View, Text, Image, ScrollView, Input, Textarea } from '@tarojs/components'
import Taro, { useDidShow } from '@tarojs/taro'
import { pageShopGoods, updateShopGoods } from '@/api/shop/shopGoods'
import { listShopGoodsCategory } from '@/api/shop/shopGoodsCategory'
import type { ShopGoods, ShopGoodsParam } from '@/api/shop/shopGoods/model'
import type { ShopGoodsCategory } from '@/api/shop/shopGoodsCategory/model'
definePageConfig({
navigationBarTitleText: '商品管理',
})
// ─── Tab 配置 ──────────────────────────────────────────────────
type TabKey = 'all' | 'on_sale' | 'pending' | 'sold_out'
const TABS: { key: TabKey; label: string; param: Partial<ShopGoodsParam> }[] = [
{ key: 'all', label: '全部', param: {} },
{ key: 'on_sale', label: '出售中', param: { status: 0 } },
{ key: 'pending', label: '待上架', param: { status: 1 } },
{ key: 'sold_out', label: '已售罄', param: { stock: 0 } },
]
// ─── 状态辅助函数 ──────────────────────────────────────────────────
function getStatusText(status?: number): string {
const map: Record<number, string> = { 0: '已上架', 1: '待上架', 2: '待审核', 3: '审核不通过' }
return map[status ?? 0] || '未知'
}
function getStatusColor(status?: number): string {
const map: Record<number, string> = { 0: '#16a34a', 1: '#ea580c', 2: '#9333ea', 3: '#dc2626' }
return map[status ?? 0] || '#999'
}
// ─── 页面组件 ──────────────────────────────────────────────────────
export default function StoreGoodsPage() {
const [activeTab, setActiveTab] = useState<TabKey>('all')
// 商品列表与分页
const [goodsList, setGoodsList] = useState<ShopGoods[]>([])
const [page, setPage] = useState(1)
const [hasMore, setHasMore] = useState(true)
const [loading, setLoading] = useState(false)
// 搜索与筛选
const [keyword, setKeyword] = useState('')
const [searchText, setSearchText] = useState('')
const [categories, setCategories] = useState<ShopGoodsCategory[]>([])
const [selectedCategory, setSelectedCategory] = useState<number | undefined>(undefined)
const [showCategoryPicker, setShowCategoryPicker] = useState(false)
// 编辑弹窗
const [showEditModal, setShowEditModal] = useState(false)
const [editGoods, setEditGoods] = useState<ShopGoods | null>(null)
const [editForm, setEditForm] = useState({
price: '',
salePrice: '',
stock: '',
sortNumber: '',
comments: '',
})
const [submitting, setSubmitting] = useState(false)
const pageSize = 10
const loadingRef = useRef(false)
/** 加载商品列表 */
const loadGoods = useCallback(async (tab: TabKey, pageNo: number = 1, append = false) => {
if (loadingRef.current) return
loadingRef.current = true
setLoading(true)
try {
const tabConfig = TABS.find(t => t.key === tab)
if (!tabConfig) return
const params: ShopGoodsParam = {
...tabConfig.param,
page: pageNo,
limit: pageSize,
}
if (keyword) params.keywords = keyword
if (selectedCategory) params.categoryId = selectedCategory
const res = await pageShopGoods(params)
const list = res?.list || []
// 已售罄 Tab 需要客户端二次过滤 stock <= 0
const filtered = tab === 'sold_out' ? list.filter(g => (g.stock ?? 0) <= 0) : list
setGoodsList(prev => append ? [...prev, ...filtered] : filtered)
setPage(pageNo)
setHasMore(list.length >= pageSize)
} catch (e: any) {
Taro.showToast({ title: e.message || '加载失败', icon: 'none' })
if (!append) setGoodsList([])
} finally {
loadingRef.current = false
setLoading(false)
}
}, [keyword, selectedCategory])
// 加载分类列表
useEffect(() => {
listShopGoodsCategory({})
.then(data => setCategories(data || []))
.catch(() => {})
}, [])
// 切换 tab 时重新加载
useEffect(() => {
loadGoods(activeTab, 1)
}, [activeTab, loadGoods])
// 页面重新显示时刷新
useDidShow(() => {
loadGoods(activeTab, 1)
})
/** 加载更多 */
const handleLoadMore = () => {
if (hasMore && !loadingRef.current) {
loadGoods(activeTab, page + 1, true)
}
}
/** 执行搜索 */
const handleSearch = () => {
setKeyword(searchText)
loadGoods(activeTab, 1)
}
/** 选择分类 */
const handleSelectCategory = (cat: ShopGoodsCategory | undefined) => {
setSelectedCategory(cat?.categoryId)
setShowCategoryPicker(false)
loadGoods(activeTab, 1)
}
/** 快速上架/下架 */
const handleToggleStatus = async (goods: ShopGoods) => {
const newStatus = goods.status === 0 ? 1 : 0
const actionText = newStatus === 0 ? '上架' : '下架'
Taro.showModal({
title: '提示',
content: `确定要${actionText}商品「${goods.name}」吗?`,
success: async (res) => {
if (!res.confirm) return
try {
Taro.showLoading({ title: `${actionText}中...` })
await updateShopGoods({ ...goods, status: newStatus })
Taro.hideLoading()
Taro.showToast({ title: `${actionText}成功`, icon: 'success' })
loadGoods(activeTab, 1)
} catch (e: any) {
Taro.hideLoading()
Taro.showToast({ title: e.message || `${actionText}失败`, icon: 'none' })
}
},
})
}
/** 切换推荐 */
const handleToggleRecommend = async (goods: ShopGoods) => {
const newRecommend = goods.recommend === 1 ? 0 : 1
try {
await updateShopGoods({ ...goods, recommend: newRecommend })
Taro.showToast({ title: newRecommend === 1 ? '已推荐' : '已取消推荐', icon: 'none' })
loadGoods(activeTab, 1)
} catch (e: any) {
Taro.showToast({ title: e.message || '操作失败', icon: 'none' })
}
}
/** 打开编辑弹窗 */
const openEditModal = (goods: ShopGoods) => {
setEditGoods(goods)
setEditForm({
price: goods.price || '',
salePrice: goods.salePrice || '',
stock: String(goods.stock ?? ''),
sortNumber: String(goods.sortNumber ?? ''),
comments: goods.comments || '',
})
setShowEditModal(true)
}
/** 关闭编辑弹窗 */
const closeEditModal = () => {
setShowEditModal(false)
setEditGoods(null)
}
/** 提交编辑 */
const submitEdit = async () => {
if (!editGoods) return
const price = parseFloat(editForm.price)
if (isNaN(price) || price < 0) {
Taro.showToast({ title: '请输入有效的价格', icon: 'none' })
return
}
const stock = parseInt(editForm.stock, 10)
if (isNaN(stock) || stock < 0) {
Taro.showToast({ title: '请输入有效的库存', icon: 'none' })
return
}
setSubmitting(true)
try {
await updateShopGoods({
...editGoods,
price: editForm.price,
salePrice: editForm.salePrice || undefined,
stock,
sortNumber: parseInt(editForm.sortNumber, 10) || 0,
comments: editForm.comments || undefined,
})
Taro.showToast({ title: '保存成功', icon: 'success' })
closeEditModal()
loadGoods(activeTab, 1)
} catch (e: any) {
Taro.showToast({ title: e.message || '保存失败', icon: 'none' })
} finally {
setSubmitting(false)
}
}
/** 渲染单个商品卡片 */
const renderGoodsCard = (goods: ShopGoods) => {
const isSoldOut = (goods.stock ?? 0) <= 0
const isOnSale = goods.status === 0
return (
<View key={goods.goodsId} className='bg-white rounded-xl mx-3 mt-3 p-4'>
{/* 商品头部:图片 + 名称 + 状态 */}
<View className='flex items-start gap-3 mb-3'>
{goods.image ? (
<Image
className='w-20 h-20 rounded-lg bg-gray-50 flex-shrink-0'
src={goods.image}
mode='aspectFill'
onClick={() => Taro.previewImage({ urls: [goods.image!] })}
/>
) : (
<View className='w-20 h-20 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0'>
<Text className='text-2xl text-gray-300'>📦</Text>
</View>
)}
<View className='flex-1 min-w-0'>
<View className='flex items-center gap-2 mb-1'>
<Text
className='text-xs px-1.5 py-0.5 rounded'
style={{ color: getStatusColor(goods.status), background: getStatusColor(goods.status) + '15' }}
>
{getStatusText(goods.status)}
</Text>
{isSoldOut && isOnSale && (
<Text className='text-xs px-1.5 py-0.5 rounded bg-red-50 text-red-500'></Text>
)}
{goods.recommend === 1 && (
<Text className='text-xs px-1.5 py-0.5 rounded bg-amber-50 text-amber-600'></Text>
)}
</View>
<Text className='text-sm text-gray-800 block' style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
{goods.name}
</Text>
{goods.categoryName && (
<Text className='text-xs text-gray-400 mt-1 block'>{goods.categoryName}</Text>
)}
</View>
</View>
{/* 价格信息 */}
<View className='flex items-center gap-4 mb-3 bg-gray-50 rounded-lg p-3'>
<View className='flex-1'>
<Text className='text-xs text-gray-400 block'></Text>
<Text className='text-base text-red-500 font-medium'>¥{goods.price || '0'}</Text>
</View>
{goods.salePrice && (
<View className='flex-1'>
<Text className='text-xs text-gray-400 block'></Text>
<Text className='text-sm text-gray-400 line-through'>¥{goods.salePrice}</Text>
</View>
)}
{goods.dealerPrice && (
<View className='flex-1'>
<Text className='text-xs text-gray-400 block'>VIP价</Text>
<Text className='text-sm text-purple-500'>¥{goods.dealerPrice}</Text>
</View>
)}
<View className='flex-1'>
<Text className='text-xs text-gray-400 block'></Text>
<Text className='text-sm text-gray-700'>{goods.sales || 0}</Text>
</View>
<View className='flex-1'>
<Text className='text-xs text-gray-400 block'></Text>
<Text className={`text-sm ${isSoldOut ? 'text-red-500' : 'text-gray-700'}`}>{goods.stock ?? 0}</Text>
</View>
</View>
{/* 操作按钮 */}
<View className='flex items-center gap-2 border-t border-gray-50 pt-3'>
{/* 上架/下架 */}
<View
className={`flex-1 py-2 rounded-lg text-center ${isOnSale ? 'bg-orange-50' : 'bg-green-50'}`}
onClick={() => handleToggleStatus(goods)}
>
<Text className={`text-sm ${isOnSale ? 'text-orange-500' : 'text-green-500'}`}>
{isOnSale ? '下架' : '上架'}
</Text>
</View>
{/* 推荐 */}
<View
className={`flex-1 py-2 rounded-lg text-center ${goods.recommend === 1 ? 'bg-amber-50' : 'bg-gray-50'}`}
onClick={() => handleToggleRecommend(goods)}
>
<Text className={`text-sm ${goods.recommend === 1 ? 'text-amber-600' : 'text-gray-500'}`}>
{goods.recommend === 1 ? '取消推荐' : '推荐'}
</Text>
</View>
{/* 编辑 */}
<View
className='flex-1 py-2 rounded-lg text-center bg-blue-50'
onClick={() => openEditModal(goods)}
>
<Text className='text-sm text-blue-500'></Text>
</View>
</View>
</View>
)
}
return (
<View className='min-h-full bg-gray-50'>
{/* 搜索栏 */}
<View className='bg-white px-3 py-2 flex items-center gap-2'>
<View className='flex-1 flex items-center bg-gray-100 rounded-lg px-3 py-1.5'>
<Input
className='flex-1 text-sm'
placeholder='搜索商品名称'
value={searchText}
onInput={(e) => setSearchText(e.detail.value)}
onConfirm={handleSearch}
confirmType='search'
/>
{searchText ? (
<Text
className='text-gray-400 text-lg px-1'
onClick={() => { setSearchText(''); setKeyword(''); loadGoods(activeTab, 1) }}
>×</Text>
) : null}
</View>
<View
className='px-3 py-1.5 rounded-lg bg-gray-100 flex items-center'
onClick={() => setShowCategoryPicker(true)}
>
<Text className='text-sm text-gray-600'>
{selectedCategory
? categories.find(c => c.categoryId === selectedCategory)?.title || '分类'
: '分类'}
</Text>
<Text className='text-gray-400 text-xs ml-1'></Text>
</View>
</View>
{/* Tab 栏 */}
<View className='bg-white flex border-b border-gray-50'>
{TABS.map(tab => (
<View
key={tab.key}
className={`flex-1 text-center py-3 border-b-2 ${
activeTab === tab.key ? 'border-cyan-500' : 'border-transparent'
}`}
onClick={() => setActiveTab(tab.key)}
>
<Text
className={`text-sm ${activeTab === tab.key ? 'text-cyan-500 font-medium' : 'text-gray-500'}`}
>
{tab.label}
</Text>
</View>
))}
</View>
{/* 商品列表 */}
<ScrollView
scrollY
style={{ height: 'calc(100vh - 100px)' }}
onScrollToLower={handleLoadMore}
lowerThreshold={100}
>
{loading && goodsList.length === 0 ? (
<View className='flex justify-center items-center py-20'>
<Text className='text-gray-400'>...</Text>
</View>
) : goodsList.length === 0 ? (
<View className='flex justify-center items-center py-20'>
<Text className='text-gray-400'></Text>
</View>
) : (
goodsList.map(renderGoodsCard)
)}
{loading && goodsList.length > 0 && (
<View className='flex justify-center items-center py-4'>
<Text className='text-gray-400 text-sm'>...</Text>
</View>
)}
{!hasMore && goodsList.length > 0 && (
<View className='flex justify-center items-center py-4'>
<Text className='text-gray-300 text-xs'></Text>
</View>
)}
<View className='h-6' />
</ScrollView>
{/* 分类选择弹窗 */}
{showCategoryPicker && (
<View className='fixed inset-0 z-50' onClick={() => setShowCategoryPicker(false)}>
<View className='absolute inset-0 bg-black/50' />
<View
className='absolute bottom-0 left-0 right-0 bg-white rounded-t-2xl max-h-[60vh] overflow-y-auto'
onClick={(e) => e.stopPropagation()}
>
<View className='sticky top-0 bg-white border-b border-gray-50 px-5 py-4 flex justify-between items-center'>
<Text className='text-base font-medium text-gray-800'></Text>
<Text className='text-gray-400 text-lg' onClick={() => setShowCategoryPicker(false)}>×</Text>
</View>
<View
className='px-5 py-3.5 border-b border-gray-50'
onClick={() => handleSelectCategory(undefined)}
>
<Text className='text-sm text-gray-600'></Text>
</View>
{categories.map(cat => (
<View
key={cat.categoryId}
className='px-5 py-3.5 border-b border-gray-50 flex justify-between items-center'
onClick={() => handleSelectCategory(cat)}
>
<Text className='text-sm text-gray-700'>{cat.title}</Text>
{selectedCategory === cat.categoryId && (
<Text className='text-cyan-500'></Text>
)}
</View>
))}
<View className='h-8' />
</View>
</View>
)}
{/* 编辑弹窗 */}
{showEditModal && editGoods && (
<View className='fixed inset-0 z-50 flex items-end justify-center'>
<View className='absolute inset-0 bg-black/50' onClick={closeEditModal} />
<View className='relative bg-white rounded-t-2xl w-full px-5 pt-6 pb-10 max-h-[80vh] overflow-y-auto'>
<Text className='text-lg font-medium text-gray-800 text-center mb-5 block'></Text>
{/* 商品信息 */}
<View className='bg-gray-50 rounded-xl p-4 mb-5 flex items-center gap-3'>
{editGoods.image && (
<Image className='w-14 h-14 rounded-lg bg-gray-100' src={editGoods.image} mode='aspectFill' />
)}
<View className='flex-1 min-w-0'>
<Text className='text-sm text-gray-800 block' style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
{editGoods.name}
</Text>
<Text className='text-xs text-gray-400 mt-1 block'>ID: {editGoods.goodsId}</Text>
</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='请输入价格'
value={editForm.price}
onInput={(e) => setEditForm(prev => ({ ...prev, price: e.detail.value }))}
/>
</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='选填,划线价'
value={editForm.salePrice}
onInput={(e) => setEditForm(prev => ({ ...prev, salePrice: e.detail.value }))}
/>
</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='number'
placeholder='请输入库存数量'
value={editForm.stock}
onInput={(e) => setEditForm(prev => ({ ...prev, stock: e.detail.value }))}
/>
</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='number'
placeholder='数字越小越靠前'
value={editForm.sortNumber}
onInput={(e) => setEditForm(prev => ({ ...prev, sortNumber: e.detail.value }))}
/>
</View>
<View className='mb-6'>
<Text className='text-sm text-gray-600 mb-2 block'></Text>
<Textarea
className='bg-gray-50 rounded-lg px-4 py-3 text-sm w-full'
placeholder='选填'
value={editForm.comments}
onInput={(e) => setEditForm(prev => ({ ...prev, comments: e.detail.value }))}
maxlength={200}
style={{ minHeight: '60px' }}
/>
</View>
{/* 操作按钮 */}
<View className='flex gap-3'>
<View className='flex-1 py-3 rounded-xl bg-gray-100 text-center' onClick={closeEditModal}>
<Text className='text-sm text-gray-600'></Text>
</View>
<View
className='flex-1 py-3 rounded-xl bg-cyan-500 text-center'
onClick={submitting ? undefined : submitEdit}
>
<Text className='text-sm text-white'>
{submitting ? '保存中...' : '保存'}
</Text>
</View>
</View>
</View>
</View>
)}
</View>
)
}