diff --git a/pages/mall/merchant/chat.uvue b/pages/mall/merchant/chat.uvue
index 53bd6ed9..c345078a 100644
--- a/pages/mall/merchant/chat.uvue
+++ b/pages/mall/merchant/chat.uvue
@@ -1,11 +1,17 @@
+<<<<<<< HEAD
+=======
+
+
+
+
+
+
+
+
+
+
+ 已接入客户对话
+
+
+
+
+
+
+
+
+ {{ chatTitle }}
+
+ {{ message.content }}
+
+ {{ message.time }}
+
+
+
+
+
+
+
+
+ {{ message.content }}
+
+ {{ message.time }}
+
+
+
+>>>>>>> local-backup-root-cyj
+<<<<<<< HEAD
➤
+=======
+
+
+
+ 😊
+ 📷
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ emoji }}
+
+
+
+>>>>>>> local-backup-root-cyj
diff --git a/pages/mall/merchant/exclusive-discounts.uvue b/pages/mall/merchant/exclusive-discounts.uvue
new file mode 100644
index 00000000..b47eeea8
--- /dev/null
+++ b/pages/mall/merchant/exclusive-discounts.uvue
@@ -0,0 +1,544 @@
+
+
+
+
+
+
+
+
+
+ 该客户暂无专属折扣商品
+
+
+
+
+
+ {{ item.product_name }}
+
+ 原价 ¥{{ item.base_price }}
+ 当前设置: {{ parseFloat(item.discount_rate) * 10 }} 折
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{p.name}}
+ ¥{{p.base_price}}
+
+
+ 暂无可选商品
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/mall/merchant/index.uvue b/pages/mall/merchant/index.uvue
index 753a0a28..96ead0d7 100644
--- a/pages/mall/merchant/index.uvue
+++ b/pages/mall/merchant/index.uvue
@@ -1,3 +1,4 @@
+<<<<<<< HEAD
@@ -134,6 +135,245 @@
+=======
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 📋
+
+ {{ todayStats.orders || 0 }}
+ 订单数
+
+
+
+ 💰
+
+ ¥{{ formatNumber(todayStats.sales) }}
+ 销售额
+
+
+
+ 👥
+
+ {{ todayStats.visitors || 0 }}
+ 访客数
+
+
+
+ 📈
+
+ {{ todayStats.conversion || 0 }}%
+ 转化率
+
+
+
+
+
+
+
+
+
+
+ 📦
+
+
+ {{ pendingCounts.pending_shipment }}
+ 待发货
+
+
+
+
+ ↩️
+
+
+ {{ pendingCounts.refund_requests }}
+ 退款
+
+
+
+
+ ⚠️
+
+
+ {{ pendingCounts.low_stock }}
+ 库存预警
+
+
+
+
+ 💬
+
+
+ {{ pendingCounts.pending_reviews }}
+ 待回复
+
+
+
+
+
+
+
+
+
+
+
+ ➕
+
+ 发布商品
+
+
+
+ 📋
+
+ 订单管理
+
+
+
+ 📦
+
+ 商品管理
+
+
+
+ 📊
+
+ 库存管理
+
+
+
+ 🎯
+
+ 营销活动
+
+
+
+ 📈
+
+ 数据统计
+
+
+
+ 💰
+
+ 财务结算
+
+
+
+ VIP
+
+ 会员管理
+
+
+
+ 🏪
+
+ 店铺设置
+
+
+
+
+
+
+
+
+ 📭
+ 暂无订单
+ 有新订单时会在这里显示
+
+
+
+
+
+
+
+
+ {{ item.product_name }}
+
+ {{ item.sku_name }}
+ ×{{ item.quantity }}
+
+
+ ¥{{ item.price }}
+
+
+ 还有{{ order.items.length - 3 }}件商品
+
+
+
+
+
+
+
+
+
+
+
+>>>>>>> local-backup-root-cyj
@@ -219,7 +459,20 @@
low_stock: 0,
pending_reviews: 0
} as PendingCountsType,
+<<<<<<< HEAD
recentOrders: [] as OrderType[]
+=======
+ recentOrders: [] as OrderType[],
+ unreadCount: 0,
+ refreshing: false
+ }
+ },
+
+ computed: {
+ currentDate(): string {
+ const now = new Date()
+ return `${now.getMonth() + 1}月${now.getDate()}日`
+>>>>>>> local-backup-root-cyj
}
},
@@ -229,6 +482,7 @@
onShow() {
if (this.merchantId) {
+<<<<<<< HEAD
this.loadMerchantData()
this.loadTodayStats()
this.loadPendingCounts()
@@ -239,16 +493,36 @@
this.loadTodayStats()
this.loadPendingCounts()
this.loadRecentOrders()
+=======
+ this.loadAllData()
+ this.startRealtimeSubscription()
+ } else {
+ setTimeout(() => {
+ this.loadAllData()
+ this.startRealtimeSubscription()
+>>>>>>> local-backup-root-cyj
}, 500)
}
},
+<<<<<<< HEAD
methods: {
formatNumber(value: number | null): string {
if (value == null) return '0.00'
return value.toFixed(2)
},
+=======
+ onHide() {
+ this.stopRealtimeSubscription()
+ },
+
+ onUnload() {
+ this.stopRealtimeSubscription()
+ },
+
+ methods: {
+>>>>>>> local-backup-root-cyj
async initMerchantId() {
try {
const session = supa.getSession()
@@ -263,6 +537,56 @@
}
},
+<<<<<<< HEAD
+=======
+ startRealtimeSubscription() {
+ if (!this.merchantId) return
+
+ // 监听订单表的变化
+ try {
+ supa.channel('ml_orders_realtime')
+ .on('postgres_changes', {
+ event: '*',
+ schema: 'public',
+ table: 'ml_orders',
+ filter: `merchant_id=eq.${this.merchantId}`
+ }, (payload) => {
+ console.log('收到订单实时更新:', payload)
+ // 延迟一下再刷新,避免连续变动导致频繁请求
+ setTimeout(() => {
+ this.loadTodayStats()
+ this.loadPendingCounts()
+ this.loadRecentOrders()
+ }, 500)
+ })
+ .subscribe()
+ } catch (e) {
+ console.error('订阅实时更新失败:', e)
+ }
+ },
+
+ stopRealtimeSubscription() {
+ try {
+ supa.channel('ml_orders_realtime').unsubscribe()
+ } catch (e) {
+ console.error('取消订阅失败:', e)
+ }
+ },
+
+ async loadAllData() {
+ await this.loadMerchantData()
+ await this.loadTodayStats()
+ await this.loadPendingCounts()
+ await this.loadRecentOrders()
+ await this.loadUnreadCount()
+ },
+
+ formatNumber(value: number | null): string {
+ if (value == null) return '0.00'
+ return value.toFixed(2)
+ },
+
+>>>>>>> local-backup-root-cyj
async loadMerchantData() {
try {
const response = await supa
@@ -272,7 +596,12 @@
.limit(1)
.execute()
+<<<<<<< HEAD
if (response.error != null || !response.data || (response.data as any[]).length === 0) {
+=======
+ if (response.error != null) { console.error('ml_shops请求500报错', response.error) }
+ if (response.error != null || !response.data || (response.data as any[]).length === 0) {
+>>>>>>> local-backup-root-cyj
this.shopInfo = {
id: null,
merchant_id: this.merchantId,
@@ -291,7 +620,11 @@
const rawData = (response.data as any[])[0] as UTSJSONObject
this.shopInfo = {
+<<<<<<< HEAD
id: rawData.getString('id') || null,
+=======
+ id: rawData.getString('id') || null,
+>>>>>>> local-backup-root-cyj
merchant_id: rawData.getString('merchant_id') || null,
shop_name: rawData.getString('shop_name') || '我的店铺',
shop_logo: rawData.getString('shop_logo') || null,
@@ -303,6 +636,43 @@
total_sales: rawData.getNumber('total_sales') || 0,
status: rawData.getNumber('status') || 1
}
+<<<<<<< HEAD
+=======
+
+ // 重新动态查询并计算该店铺下所有商品的真实销量总和
+ try {
+ const salesRes = await supa
+ .from('ml_products')
+ .select('sale_count')
+ .eq('merchant_id', this.merchantId)
+ .execute()
+
+ if (salesRes.error != null) { console.error('ml_products sale_count报错', salesRes.error) }
+ if (salesRes.data != null) {
+ let calcTotalSales: number = 0
+ const salesData = salesRes.data as any[]
+ for (let i = 0; i < salesData.length; i++) {
+ const productInfo = salesData[i] as UTSJSONObject
+ const currentSale = productInfo.getNumber('sale_count')
+ if (currentSale != null) {
+ calcTotalSales += currentSale
+ }
+ }
+
+ let baseSales: number = 0
+ if (this.shopInfo.total_sales != null) {
+ baseSales = Number(this.shopInfo.total_sales)
+ }
+
+ if (calcTotalSales > baseSales) {
+ this.shopInfo.total_sales = calcTotalSales
+ }
+ }
+ } catch (e) {
+ console.error('获取店铺真实销量失败:', e)
+ }
+
+>>>>>>> local-backup-root-cyj
} catch (e) {
console.error('加载店铺信息失败:', e)
}
@@ -310,6 +680,7 @@
async loadTodayStats() {
try {
+<<<<<<< HEAD
const response = await supa
.from('ml_orders')
.select('total_amount, order_status', { count: 'exact' })
@@ -323,24 +694,89 @@
let totalOrders = 0
let totalSales = 0
+=======
+ // 1. 获取所有订单
+ const response = await supa
+ .from('ml_orders')
+ .select(`
+ total_amount,
+ order_status,
+ created_at,
+ order_items (quantity)
+ `)
+ .eq('merchant_id', this.merchantId)
+ .execute()
+
+ if (response.error != null) { console.error('ml_orders stats报错', response.error); return }
+
+ let todayOrders = 0
+ let todaySales = 0
+ let allTimeSalesVolume = 0 // 总销量(件数)
+
+ const now = new Date()
+ // 获取今日0点的毫秒数 (本地时间)
+ const todayStartMs = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime()
+>>>>>>> local-backup-root-cyj
const rawData = response.data as any[]
if (rawData != null) {
for (let i = 0; i < rawData.length; i++) {
const item = rawData[i] as UTSJSONObject
const status = item.getNumber('order_status')
+<<<<<<< HEAD
if (status >= 2) {
totalOrders++
totalSales += item.getNumber('total_amount') || 0
+=======
+
+ // 有效订单(已支付、已发货、已完成) >= 2
+ // 如果是退款(0)或取消(5),可能不计入今日销售额,这里按需调整
+ if (status != null && status >= 2 && status < 5) {
+ // 计算总销量(即售出的商品总件数)
+ const itemsObj = item.get('order_items')
+ if (itemsObj != null && Array.isArray(itemsObj)) {
+ const itemsArr = itemsObj as any[]
+ for (let j = 0; j < itemsArr.length; j++) {
+ const orderItem = itemsArr[j] as UTSJSONObject
+ allTimeSalesVolume += Math.floor(orderItem.getNumber('quantity') || 1)
+ }
+ } else {
+ allTimeSalesVolume += 1
+ }
+
+ // 判断是否是今日数据
+ const createdAtStr = item.getString('created_at') || ''
+ if (createdAtStr.length > 0) {
+ const orderDateMs = new Date(createdAtStr).getTime()
+ if (orderDateMs >= todayStartMs) {
+ todayOrders++
+ todaySales += item.getNumber('total_amount') || 0
+ }
+ }
+>>>>>>> local-backup-root-cyj
}
}
}
+<<<<<<< HEAD
this.todayStats = {
orders: totalOrders,
sales: totalSales,
visitors: Math.floor(totalOrders * 3),
conversion: totalOrders > 0 ? 15 : 0
+=======
+ // 更新店铺总销量显示
+ let currentShopSales = Number(this.shopInfo.total_sales || 0)
+ if (allTimeSalesVolume > currentShopSales) {
+ this.shopInfo.total_sales = allTimeSalesVolume
+ }
+
+ this.todayStats = {
+ orders: todayOrders,
+ sales: todaySales,
+ visitors: Math.floor(todayOrders * (2.5 + Math.random())) + 5, // 模拟访客数
+ conversion: todayOrders > 0 ? (12 + Math.floor(Math.random() * 8)) : 0 // 模拟转化率
+>>>>>>> local-backup-root-cyj
}
} catch (e) {
console.error('获取今日统计异常:', e)
@@ -356,22 +792,39 @@
.eq('order_status', 2)
.execute()
+<<<<<<< HEAD
const refundRes = await supa
+=======
+ if (pendingShipmentRes.error != null) { console.error('pendingShipment报错', pendingShipmentRes.error) }
+ const refundRes = await supa
+>>>>>>> local-backup-root-cyj
.from('ml_orders')
.select('id', { count: 'exact' })
.eq('merchant_id', this.merchantId)
.eq('order_status', 0)
.execute()
+<<<<<<< HEAD
const lowStockRes = await supa
+=======
+ if (refundRes.error != null) { console.error('refundRes报错', refundRes.error) }
+ const lowStockRes = await supa
+>>>>>>> local-backup-root-cyj
.from('ml_products')
.select('id', { count: 'exact' })
.eq('merchant_id', this.merchantId)
.lte('total_stock', 10)
+<<<<<<< HEAD
.gte('total_stock', 0)
.execute()
this.pendingCounts = {
+=======
+ .execute()
+
+ if (lowStockRes.error != null) { console.error('lowStockRes报错', lowStockRes.error) }
+ this.pendingCounts = {
+>>>>>>> local-backup-root-cyj
pending_shipment: pendingShipmentRes.total || 0,
refund_requests: refundRes.total || 0,
low_stock: lowStockRes.total || 0,
@@ -388,7 +841,11 @@
.from('ml_orders')
.select(`
*,
+<<<<<<< HEAD
order_items!inner (
+=======
+ order_items (
+>>>>>>> local-backup-root-cyj
id,
product_id,
product_name,
@@ -403,10 +860,15 @@
.limit(5)
.execute()
+<<<<<<< HEAD
if (response.error != null || !response.data) {
this.recentOrders = []
return
}
+=======
+ if (response.error != null) { console.error('recentOrders报错', response.error) }
+ if (response.error != null || !response.data) { this.recentOrders = []; return; }
+>>>>>>> local-backup-root-cyj
const rawData = response.data as any[]
const ordersData: OrderType[] = []
@@ -447,6 +909,7 @@
this.recentOrders = ordersData
} catch (e) {
+<<<<<<< HEAD
console.error('加载最新订单异常:', e)
}
},
@@ -472,6 +935,51 @@
case 0: return '退款中'
default: return '未知状态'
}
+=======
+ console.error('加载最新订单异常:', e); uni.showModal({title: '最新订单报错', content: e.toString()})
+ }
+ },
+
+ async loadUnreadCount() {
+ try {
+ const response = await supa
+ .from('ml_chat_messages')
+ .select('id', { count: 'exact' })
+ .eq('receiver_id', this.merchantId)
+ .eq('is_read', false)
+ .execute()
+
+ if (response.error != null) { uni.showModal({title: 'ml_chat_messages报错', content: JSON.stringify(response.error)}) }
+ this.unreadCount = response.total || 0
+ } catch (e) {
+ console.error('获取未读消息数失败:', e)
+ }
+ },
+
+ onRefresh() {
+ this.refreshing = true
+ this.loadAllData().then(() => {
+ this.refreshing = false
+ })
+ },
+
+ getOrderStatusClass(status: number): string {
+ if (status === 1) return 'status-pending'
+ if (status === 2) return 'status-paid'
+ if (status === 3) return 'status-shipped'
+ if (status === 4) return 'status-completed'
+ if (status === 0) return 'status-refund'
+ return 'status-default'
+ },
+
+ getOrderStatusText(status: number): string {
+ if (status === 1) return '待付款'
+ if (status === 2) return '待发货'
+ if (status === 3) return '已发货'
+ if (status === 4) return '已完成'
+ if (status === 0) return '退款中'
+ return '未知'
+>>>>>>> local-backup-root-cyj
},
formatTime(timeStr: string): string {
@@ -480,6 +988,7 @@
const now = new Date()
const diff = now.getTime() - date.getTime()
const minutes = Math.floor(diff / (1000 * 60))
+<<<<<<< HEAD
if (minutes < 60) {
return `${minutes}分钟前`
@@ -542,12 +1051,67 @@
uni.navigateTo({
url: `/pages/mall/merchant/order-detail?id=${orderId}`
})
+=======
+
+ if (minutes < 60) return `${minutes}分钟前`
+ if (minutes < 1440) return `${Math.floor(minutes / 60)}小时前`
+ return `${date.getMonth() + 1}-${date.getDate()}`
+ },
+
+ goToMessages() {
+ uni.navigateTo({ url: '/pages/mall/merchant/messages' })
+ },
+
+ goToSettings() {
+ uni.navigateTo({ url: '/pages/mall/merchant/shop-edit' })
+ },
+
+ goToOrders(type: string) {
+ uni.navigateTo({ url: `/pages/mall/merchant/orders?type=${type}` })
+ },
+
+ goToProducts(type: string) {
+ if (type === 'add') {
+ uni.navigateTo({ url: '/pages/mall/merchant/product-edit' })
+ } else {
+ uni.navigateTo({ url: '/pages/mall/merchant/products' })
+ }
+ },
+
+ goToPromotions() {
+ uni.navigateTo({ url: '/pages/mall/merchant/promotions' })
+ },
+
+ goToStatistics() {
+ uni.navigateTo({ url: '/pages/mall/merchant/statistics' })
+ },
+
+ goToFinance() {
+ uni.navigateTo({ url: '/pages/mall/merchant/finance' })
+ },
+
+ goToReviews() {
+ uni.navigateTo({ url: '/pages/mall/merchant/reviews' })
+ },
+
+ goToInventory() {
+ uni.navigateTo({ url: '/pages/mall/merchant/inventory' })
+ },
+
+ goToMembers() {
+ uni.navigateTo({ url: '/pages/mall/merchant/members' })
+ },
+
+ goToOrderDetail(orderId: string) {
+ uni.navigateTo({ url: `/pages/mall/merchant/order-detail?id=${orderId}` })
+>>>>>>> local-backup-root-cyj
}
}
}
+=======
+ .merchant-container { background-color: #f5f7fa; min-height: 100vh; }
+ .main-scroll { height: 100vh; }
+
+ .header { position: relative; padding-bottom: 30rpx; }
+ .header-bg { position: absolute; top: 0; left: 0; right: 0; height: 300rpx; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 0 0 40rpx 40rpx; }
+ .header-content { position: relative; padding: 40rpx 30rpx 0; }
+ .shop-info { display: flex; flex-direction: row; align-items: center; }
+ .shop-logo { width: 110rpx; height: 110rpx; border-radius: 20rpx; border-width: 4rpx; border-style: solid; border-color: rgba(255,255,255,0.8); margin-right: 24rpx; background-color: #fff; }
+ .shop-details { flex: 1; }
+ .shop-name { font-size: 40rpx; font-weight: bold; color: #fff; margin-bottom: 12rpx; }
+ .shop-meta { display: flex; flex-direction: row; align-items: center; }
+ .meta-item { display: flex; flex-direction: row; align-items: center; }
+ .meta-icon { font-size: 24rpx; margin-right: 6rpx; }
+ .meta-value { font-size: 26rpx; color: rgba(255,255,255,0.9); }
+ .meta-divider { width: 2rpx; height: 24rpx; background-color: rgba(255,255,255,0.3); margin-left: 20rpx; margin-right: 20rpx; }
+ .header-actions { display: flex; flex-direction: row; }
+ .action-btn { position: relative; margin-left: 24rpx; width: 72rpx; height: 72rpx; background-color: rgba(255,255,255,0.2); border-radius: 36rpx; display: flex; align-items: center; justify-content: center; }
+ .action-icon { font-size: 36rpx; }
+ .action-badge { position: absolute; top: -4rpx; right: -4rpx; min-width: 36rpx; height: 36rpx; background-color: #FF3B30; border-radius: 18rpx; display: flex; align-items: center; justify-content: center; padding-left: 8rpx; padding-right: 8rpx; border-width: 2rpx; border-style: solid; border-color: #fff; }
+ .action-badge-text { font-size: 20rpx; color: #fff; font-weight: bold; }
+
+ .content-area { padding-left: 24rpx; padding-right: 24rpx; padding-bottom: 30rpx; margin-top: 10rpx; }
+
+ .stats-card { background-color: #fff; border-radius: 24rpx; padding: 28rpx; margin-bottom: 24rpx; }
+ .stats-header { margin-bottom: 24rpx; }
+ .stats-title-row { display: flex; flex-direction: row; justify-content: space-between; align-items: center; }
+ .stats-title { font-size: 32rpx; font-weight: bold; color: #333; }
+ .stats-date { font-size: 24rpx; color: #999; background-color: #f5f7fa; padding-top: 6rpx; padding-bottom: 6rpx; padding-left: 16rpx; padding-right: 16rpx; border-radius: 12rpx; }
+ .stats-grid { display: flex; flex-direction: row; justify-content: space-between; }
+ .stats-item { width: 160rpx; display: flex; flex-direction: column; align-items: center; }
+ .stats-icon-wrap { width: 64rpx; height: 64rpx; border-radius: 16rpx; display: flex; align-items: center; justify-content: center; margin-bottom: 12rpx; }
+ .stats-icon-wrap.blue { background-color: #E3F2FD; }
+ .stats-icon-wrap.green { background-color: #E8F5E9; }
+ .stats-icon-wrap.orange { background-color: #FFF3E0; }
+ .stats-icon-wrap.purple { background-color: #F3E5F5; }
+ .stats-icon { font-size: 28rpx; }
+ .stats-value { font-size: 36rpx; font-weight: bold; color: #333; margin-bottom: 4rpx; }
+ .stats-label { font-size: 24rpx; color: #999; }
+
+ .section-card { background-color: #fff; border-radius: 24rpx; padding: 28rpx; margin-bottom: 24rpx; }
+ .section-header { display: flex; flex-direction: row; justify-content: space-between; align-items: center; margin-bottom: 24rpx; }
+ .section-title { font-size: 32rpx; font-weight: bold; color: #333; }
+ .section-more { font-size: 26rpx; color: #007AFF; padding-top: 8rpx; padding-bottom: 8rpx; padding-left: 16rpx; padding-right: 16rpx; background-color: #E3F2FD; border-radius: 12rpx; }
+
+ .pending-grid { display: flex; flex-direction: row; justify-content: space-between; }
+ .pending-item { width: 160rpx; display: flex; flex-direction: column; align-items: center; padding-top: 16rpx; padding-bottom: 16rpx; }
+ .pending-icon-wrap { width: 88rpx; height: 88rpx; border-radius: 24rpx; display: flex; align-items: center; justify-content: center; margin-bottom: 12rpx; }
+ .pending-icon-wrap.orange { background-color: #FFF3E0; }
+ .pending-icon-wrap.red { background-color: #FFEBEE; }
+ .pending-icon-wrap.yellow { background-color: #FFFDE7; }
+ .pending-icon-wrap.blue { background-color: #E3F2FD; }
+ .pending-icon { font-size: 40rpx; }
+ .pending-info { display: flex; flex-direction: column; align-items: center; }
+ .pending-count { font-size: 32rpx; font-weight: bold; color: #FF6B35; margin-bottom: 4rpx; }
+ .pending-text { font-size: 24rpx; color: #666; }
+
+ .shortcuts-grid { display: flex; flex-direction: row; flex-wrap: wrap; }
+ .shortcut-item { width: 25%; display: flex; flex-direction: column; align-items: center; padding-top: 20rpx; padding-bottom: 20rpx; }
+ .shortcut-icon-wrap { width: 88rpx; height: 88rpx; border-radius: 24rpx; display: flex; align-items: center; justify-content: center; margin-bottom: 12rpx; }
+ .shortcut-icon-wrap.gradient-blue { background-color: #667eea; }
+ .shortcut-icon-wrap.gradient-orange { background-color: #f093fb; }
+ .shortcut-icon-wrap.gradient-green { background-color: #4facfe; }
+ .shortcut-icon-wrap.gradient-purple { background-color: #a18cd1; }
+ .shortcut-icon-wrap.gradient-red { background-color: #ff9a9e; }
+ .shortcut-icon-wrap.gradient-cyan { background-color: #a1c4fd; }
+ .shortcut-icon-wrap.gradient-yellow { background-color: #f6d365; }
+ .shortcut-icon-wrap.gradient-pink { background-color: #ffecd2; }
+ .shortcut-icon { font-size: 40rpx; }
+ .shortcut-text { font-size: 24rpx; color: #666; }
+
+ .empty-orders { display: flex; flex-direction: column; align-items: center; justify-content: center; padding-top: 80rpx; padding-bottom: 80rpx; }
+ .empty-icon { font-size: 100rpx; margin-bottom: 20rpx; }
+ .empty-text { font-size: 30rpx; color: #666; margin-bottom: 8rpx; }
+ .empty-hint { font-size: 24rpx; color: #999; }
+ .orders-list { display: flex; flex-direction: column; }
+ .order-card { background-color: #f9fafb; border-radius: 16rpx; padding: 24rpx; margin-bottom: 16rpx; border-width: 1rpx; border-style: solid; border-color: #eee; }
+ .order-header { display: flex; flex-direction: row; justify-content: space-between; align-items: center; margin-bottom: 20rpx; }
+ .order-no-wrap { display: flex; flex-direction: row; align-items: center; }
+ .order-label { font-size: 22rpx; color: #999; background-color: #eee; padding-top: 4rpx; padding-bottom: 4rpx; padding-left: 12rpx; padding-right: 12rpx; border-radius: 8rpx; margin-right: 12rpx; }
+ .order-no { font-size: 26rpx; color: #333; font-weight: 500; }
+ .order-status { font-size: 24rpx; padding-top: 8rpx; padding-bottom: 8rpx; padding-left: 20rpx; padding-right: 20rpx; border-radius: 20rpx; font-weight: 500; }
+ .status-pending { background-color: #FFF3E0; color: #FF9800; }
+ .status-paid { background-color: #E3F2FD; color: #2196F3; }
+ .status-shipped { background-color: #E8F5E9; color: #4CAF50; }
+ .status-completed { background-color: #F3E5F5; color: #9C27B0; }
+ .status-refund { background-color: #FFEBEE; color: #F44336; }
+ .order-goods { margin-bottom: 16rpx; }
+ .goods-item { display: flex; flex-direction: row; align-items: center; margin-bottom: 16rpx; background-color: #fff; padding: 16rpx; border-radius: 12rpx; }
+ .goods-image { width: 100rpx; height: 100rpx; border-radius: 12rpx; margin-right: 16rpx; background-color: #f5f5f5; }
+ .goods-info { flex: 1; }
+ .goods-name { font-size: 28rpx; color: #333; margin-bottom: 8rpx; font-weight: 500; }
+ .goods-bottom { display: flex; flex-direction: row; justify-content: space-between; align-items: center; }
+ .goods-spec { font-size: 22rpx; color: #999; }
+ .goods-qty { font-size: 24rpx; color: #999; }
+ .goods-price { font-size: 28rpx; color: #FF6B35; font-weight: bold; }
+ .goods-more { text-align: center; padding-top: 12rpx; padding-bottom: 12rpx; font-size: 24rpx; color: #999; background-color: #fff; border-radius: 12rpx; }
+ .order-footer { display: flex; flex-direction: row; justify-content: space-between; align-items: center; padding-top: 16rpx; border-top-width: 1rpx; border-top-style: solid; border-top-color: #eee; }
+ .order-time { font-size: 24rpx; color: #999; }
+ .order-amount-wrap { display: flex; flex-direction: row; align-items: center; }
+ .amount-label { font-size: 24rpx; color: #999; margin-right: 8rpx; }
+ .amount-value { font-size: 32rpx; font-weight: bold; color: #FF6B35; }
+
+ .safe-bottom { height: 30rpx; }
+
+
+
+
+
+
+
+>>>>>>> local-backup-root-cyj
diff --git a/pages/mall/merchant/inventory.uvue b/pages/mall/merchant/inventory.uvue
index 79a538f0..bfeddea2 100644
--- a/pages/mall/merchant/inventory.uvue
+++ b/pages/mall/merchant/inventory.uvue
@@ -57,14 +57,36 @@
当前库存
{{ currentProduct?.total_stock }}
+<<<<<<< HEAD
新库存
+=======
+
+
+ 直接设为
+ 增加
+ 减少
+
+
+
+ {{ adjustType === 'set' ? '新库存数量' : '调整数值' }}
+
+
+
+
+ 备注 (可选)
+
+>>>>>>> local-backup-root-cyj
@@ -96,7 +118,13 @@
stats: { totalProducts: 0, lowStock: 0, outOfStock: 0 },
showStockModal: false,
currentProduct: null as ProductType | null,
+<<<<<<< HEAD
newStock: ''
+=======
+ newStock: '',
+ adjustType: 'set', // 'set', 'add', 'sub'
+ stockRemark: ''
+>>>>>>> local-backup-root-cyj
}
},
@@ -105,6 +133,10 @@
},
onShow() {
+<<<<<<< HEAD
+=======
+ this.page = 1
+>>>>>>> local-backup-root-cyj
this.loadProducts()
this.loadStats()
},
@@ -113,11 +145,21 @@
async initMerchantId() {
try {
const session = supa.getSession()
+<<<<<<< HEAD
this.merchantId = session?.user?.getString('id') || uni.getStorageSync('user_id') || ''
+=======
+ if (session != null && session.user != null) {
+ this.merchantId = session.user.getString('id') || ''
+ }
+ if (!this.merchantId) {
+ this.merchantId = uni.getStorageSync('user_id') || ''
+ }
+>>>>>>> local-backup-root-cyj
} catch (e) {}
},
async loadProducts() {
+<<<<<<< HEAD
this.loading = true
try {
@@ -127,10 +169,34 @@
if (response.error != null || !response.data) {
this.products = []
+=======
+ if (this.loading && this.page === 1) return
+ this.loading = true
+
+ try {
+ let query = supa.from('ml_products')
+ .select('id, name, main_image_url, total_stock, warning_stock')
+ .eq('merchant_id', this.merchantId)
+ .order('total_stock', { ascending: true })
+ .page(this.page)
+ .limit(this.limit)
+
+ if (this.currentFilter === 'low') {
+ query = query.lte('total_stock', 10) // 简化处理,实际应关联 warning_stock
+ } else if (this.currentFilter === 'out') {
+ query = query.eq('total_stock', 0)
+ }
+
+ const response = await query.execute()
+
+ if (response.error != null) {
+ console.error('加载商品失败:', response.error)
+>>>>>>> local-backup-root-cyj
return
}
const rawData = response.data as any[]
+<<<<<<< HEAD
let productsData: ProductType[] = []
for (let i = 0; i < rawData.length; i++) {
@@ -141,16 +207,38 @@
if (this.currentFilter === 'low' && stock > warning) continue
if (this.currentFilter === 'out' && stock > 0) continue
+=======
+ if (!rawData) return
+
+ const productsData: ProductType[] = []
+ for (let i = 0; i < rawData.length; i++) {
+ const item = rawData[i] as UTSJSONObject
+>>>>>>> local-backup-root-cyj
productsData.push({
id: item.getString('id') || '',
name: item.getString('name') || '',
main_image_url: item.getString('main_image_url') || '',
+<<<<<<< HEAD
total_stock: stock,
warning_stock: warning
})
}
this.products = productsData
+=======
+ total_stock: item.getNumber('total_stock') || 0,
+ warning_stock: item.getNumber('warning_stock') || 10
+ } as ProductType)
+ }
+
+ if (this.page === 1) {
+ this.products = productsData
+ } else {
+ this.products = [...this.products, ...productsData]
+ }
+
+ this.hasMore = rawData.length === this.limit
+>>>>>>> local-backup-root-cyj
} catch (e) {
console.error('加载失败:', e)
} finally {
@@ -200,7 +288,13 @@
editStock(product: ProductType) {
this.currentProduct = product
+<<<<<<< HEAD
this.newStock = String(product.total_stock)
+=======
+ this.newStock = ''
+ this.adjustType = 'set'
+ this.stockRemark = ''
+>>>>>>> local-backup-root-cyj
this.showStockModal = true
},
@@ -211,6 +305,7 @@
},
async saveStock() {
+<<<<<<< HEAD
if (!this.newStock || isNaN(parseInt(this.newStock))) {
uni.showToast({ title: '请输入有效库存', icon: 'none' })
return
@@ -218,18 +313,63 @@
try {
const response = await supa.from('ml_products').update({ total_stock: parseInt(this.newStock), updated_at: new Date().toISOString() }).eq('id', this.currentProduct!.id).execute()
+=======
+ const val = parseInt(this.newStock)
+ if (isNaN(val)) {
+ uni.showToast({ title: '请输入有效数值', icon: 'none' })
+ return
+ }
+
+ let finalStock = 0
+ if (this.adjustType === 'set') {
+ finalStock = val
+ } else if (this.adjustType === 'add') {
+ finalStock = (this.currentProduct?.total_stock || 0) + val
+ } else if (this.adjustType === 'sub') {
+ finalStock = (this.currentProduct?.total_stock || 0) - val
+ }
+
+ if (finalStock < 0) {
+ uni.showToast({ title: '最终库存不能小于0', icon: 'none' })
+ return
+ }
+
+ uni.showLoading({ title: '更新中...' })
+
+ try {
+ const response = await supa.from('ml_products')
+ .update({
+ total_stock: finalStock,
+ available_stock: finalStock,
+ updated_at: new Date().toISOString()
+ })
+ .eq('id', this.currentProduct!.id)
+ .execute()
+>>>>>>> local-backup-root-cyj
if (response.error != null) {
uni.showToast({ title: '保存失败', icon: 'none' })
return
}
+<<<<<<< HEAD
uni.showToast({ title: '保存成功', icon: 'success' })
this.closeStockModal()
this.loadProducts()
this.loadStats()
} catch (e) {
uni.showToast({ title: '保存失败', icon: 'none' })
+=======
+ uni.showToast({ title: '更新成功', icon: 'success' })
+ this.closeStockModal()
+ this.page = 1
+ this.loadProducts()
+ this.loadStats()
+ } catch (e) {
+ uni.showToast({ title: '操作异常', icon: 'none' })
+ } finally {
+ uni.hideLoading()
+>>>>>>> local-backup-root-cyj
}
},
@@ -273,10 +413,17 @@
.action-btn { padding: 12rpx 24rpx; font-size: 24rpx; background-color: #E3F2FD; color: #1976D2; border-radius: 24rpx; }
.modal-mask { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0,0,0,0.5); display: flex; align-items: center; justify-content: center; z-index: 1000; }
.modal-content { width: 80%; background-color: #fff; border-radius: 16rpx; }
+<<<<<<< HEAD
.modal-header { display: flex; justify-content: space-between; align-items: center; padding: 30rpx; border-bottom: 1rpx solid #f5f5f5; }
.modal-title { font-size: 32rpx; font-weight: bold; color: #333; }
.modal-close { font-size: 44rpx; color: #999; }
.modal-body { padding: 30rpx; }
+=======
+ .modal-body { padding: 30rpx; }
+ .adjust-type { display: flex; justify-content: space-between; margin-bottom: 30rpx; }
+ .type-btn { flex: 1; height: 64rpx; line-height: 64rpx; text-align: center; font-size: 24rpx; background-color: #f5f5f5; color: #666; margin: 0 10rpx; border-radius: 32rpx; border: 1rpx solid #eee; }
+ .type-btn.active { background-color: #E3F2FD; color: #007AFF; border-color: #007AFF; }
+>>>>>>> local-backup-root-cyj
.form-item { margin-bottom: 20rpx; }
.form-item .label { font-size: 26rpx; color: #999; display: block; margin-bottom: 10rpx; }
.form-item .value { font-size: 28rpx; color: #333; }
diff --git a/pages/mall/merchant/members.uvue b/pages/mall/merchant/members.uvue
new file mode 100644
index 00000000..62cd73db
--- /dev/null
+++ b/pages/mall/merchant/members.uvue
@@ -0,0 +1,521 @@
+
+
+
+ 等级设置
+ 客户列表
+
+
+
+
+
+
+
+
+ {{ level.name }}
+ {{ (level.discount_rate * 10).toFixed(1) }}折
+
+
+ 编辑
+ 删除
+
+
+
+
+
+
+
+
+ 暂无注册客户
+
+
+
+
+ {{ user.nickname || user.username || '未设置昵称' }}
+ {{ user.tier_name }}
+
+ {{ user.email }}
+ {{ user.phone || '未绑定手机' }}
+
+
+ 设置VIP
+ 专属折扣
+
+
+
+
+
+
+
+
+ {{ currentLevel.id ? '编辑等级' : '添加等级' }}
+
+ 等级名称
+
+
+
+ 折扣率 (0-1)
+
+
+
+ 取消
+ 保存
+
+
+
+
+
+
+
+ 设置会员等级
+
+
+ {{ level.name }}
+
+
+
+ 取消
+ 确认
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/mall/merchant/messages.uvue b/pages/mall/merchant/messages.uvue
index 5aed914b..1dd4a91b 100644
--- a/pages/mall/merchant/messages.uvue
+++ b/pages/mall/merchant/messages.uvue
@@ -1,6 +1,7 @@
+<<<<<<< HEAD
会话列表
全部消息
@@ -40,6 +41,48 @@
+=======
+
+
+
+
+ ⏳
+ 加载中...
+
+
+
+ 💬
+ 暂无会话
+ 有客户消息时会在这里显示
+
+
+
+
+
+
+
+
+
+
+ {{ conv.name }}
+ {{ conv.lastTime }}
+
+
+ {{ conv.lastMessage }}
+
+ {{ conv.unread > 99 ? '99+' : conv.unread }}
+
+
+
+ ›
+
+
+
+
+>>>>>>> local-backup-root-cyj
@@ -65,6 +108,10 @@
avatar: string
lastMessage: string
lastTime: string
+<<<<<<< HEAD
+=======
+ lastTimeRaw: string
+>>>>>>> local-backup-root-cyj
unread: number
userId: string
}
@@ -72,8 +119,11 @@
export default {
data() {
return {
+<<<<<<< HEAD
currentTab: 'chat',
messages: [] as MessageType[],
+=======
+>>>>>>> local-backup-root-cyj
conversations: [] as ConversationType[],
loading: false,
refreshing: false,
@@ -111,17 +161,24 @@
const response = await query.execute()
if (response.error != null || !response.data) {
+<<<<<<< HEAD
this.messages = []
+=======
+>>>>>>> local-backup-root-cyj
this.conversations = []
return
}
const rawData = response.data as any[]
+<<<<<<< HEAD
const messagesData: MessageType[] = []
+=======
+>>>>>>> local-backup-root-cyj
const sessionMap = new Map()
for (let i = 0; i < rawData.length; i++) {
const item = rawData[i] as UTSJSONObject
+<<<<<<< HEAD
const msg: MessageType = {
id: item.getString('id') || '',
session_id: item.getString('session_id') || '',
@@ -145,20 +202,61 @@
avatar: '',
lastMessage: msg.content,
lastTime: this.formatTime(msg.created_at),
+=======
+ const isFromUser = item.getBoolean('is_from_user') || false
+ const senderId = item.getString('sender_id') || ''
+ const receiverId = item.getString('receiver_id') || ''
+
+ // is_from_user=true 表示消息来自用户,sender_id是用户ID
+ // is_from_user=false 表示消息来自商家,receiver_id是用户ID
+ const otherUserId = isFromUser ? senderId : receiverId
+
+ // 只用 otherUserId 分组,确保每个客户只有一个会话
+ if (otherUserId === '' || otherUserId === this.merchantId) continue
+
+ const isRead = item.getBoolean('is_read') || false
+ const content = item.getString('content') || ''
+ const createdAt = item.getString('created_at') || ''
+ const sessionId = item.getString('session_id') || otherUserId
+
+ if (!sessionMap.has(otherUserId)) {
+ sessionMap.set(otherUserId, {
+ sessionId: sessionId,
+ name: '客户',
+ avatar: '',
+ lastMessage: content,
+ lastTime: this.formatTime(createdAt),
+ lastTimeRaw: createdAt,
+>>>>>>> local-backup-root-cyj
unread: 0,
userId: otherUserId
})
}
+<<<<<<< HEAD
const conv = sessionMap.get(sessionId)!
conv.lastMessage = msg.content
conv.lastTime = this.formatTime(msg.created_at)
if (!msg.is_read && !msg.is_from_user) {
+=======
+ const conv = sessionMap.get(otherUserId)!
+ // 更新最后一条消息(按时间最新的)
+ if (createdAt > conv.lastTimeRaw) {
+ conv.lastMessage = content
+ conv.lastTime = this.formatTime(createdAt)
+ conv.lastTimeRaw = createdAt
+ }
+ // 未读消息:消息来自用户且未读
+ if (!isRead && isFromUser) {
+>>>>>>> local-backup-root-cyj
conv.unread++
}
}
+<<<<<<< HEAD
this.messages = messagesData
+=======
+>>>>>>> local-backup-root-cyj
this.conversations = Array.from(sessionMap.values()).sort((a, b) => b.unread - a.unread)
} catch (e) {
@@ -175,15 +273,19 @@
})
},
+<<<<<<< HEAD
switchTab(tab: string) {
this.currentTab = tab
},
+=======
+>>>>>>> local-backup-root-cyj
onRefresh() {
this.refreshing = true
this.loadMessages()
},
+<<<<<<< HEAD
viewMessage(msg: MessageType) {
if (!msg.is_read) {
supa.from('ml_chat_messages').update({ is_read: true }).eq('id', msg.id).execute()
@@ -196,6 +298,8 @@
})
},
+=======
+>>>>>>> local-backup-root-cyj
formatTime(timeStr: string): string {
if (!timeStr) return ''
const date = new Date(timeStr)
@@ -215,6 +319,7 @@
diff --git a/pages/mall/merchant/order-detail.uvue b/pages/mall/merchant/order-detail.uvue
index c5fc3d7a..a877d647 100644
--- a/pages/mall/merchant/order-detail.uvue
+++ b/pages/mall/merchant/order-detail.uvue
@@ -1,4 +1,8 @@
+<<<<<<< HEAD
+=======
+
+>>>>>>> local-backup-root-cyj
@@ -114,20 +118,32 @@
>>>>>> local-backup-root-cyj
class="action-btn primary"
@click="shipOrder"
>
去发货
>>>>>> local-backup-root-cyj
class="action-btn primary"
@click="viewLogistics"
>
查看物流
+<<<<<<< HEAD
>>>>>> local-backup-root-cyj
v-if="order.order_status === 3"
class="action-btn primary"
@click="confirmDelivery"
@@ -163,7 +179,11 @@
@change="onLogisticsChange"
>
+<<<<<<< HEAD
{{ selectedLogistics.name || '请选择物流公司' }}
+=======
+ {{ selectedLogistics?.name || '请选择物流公司' }}
+>>>>>>> local-backup-root-cyj
@@ -240,8 +260,20 @@
updated_at: '',
items: [] as OrderItemType[]
},
+<<<<<<< HEAD
addressData: {} as AddressType,
+=======
+ addressData: {
+ recipient_name: '',
+ phone: '',
+ province: '',
+ city: '',
+ district: '',
+ detail_address: ''
+ } as AddressType,
+
+>>>>>>> local-backup-root-cyj
showShipModal: false,
logisticsCompanies: [
{ name: '顺丰速运', code: 'SF' },
@@ -252,27 +284,51 @@
{ name: 'EMS', code: 'EMS' },
{ name: '京东物流', code: 'JD' }
] as LogisticsType[],
+<<<<<<< HEAD
selectedLogistics: {} as LogisticsType,
+=======
+ selectedLogistics: { name: '', code: '' } as LogisticsType,
+>>>>>>> local-backup-root-cyj
trackingNumber: ''
}
},
+<<<<<<< HEAD
onLoad(options: any) {
const id = options.id as string
if (id) {
+=======
+ onLoad(options: any) { console.log('--- DEBUG ON LOAD ---', options)
+ let id = ''
+ if (options['id'] != null) {
+ id = options['id'] as string
+ } else if (options.id != null) {
+ id = options.id as string
+ }
+
+ if (id !== '') {
+>>>>>>> local-backup-root-cyj
this.orderId = id
this.loadOrderDetail()
}
},
methods: {
+<<<<<<< HEAD
async loadOrderDetail() {
try {
+=======
+ async loadOrderDetail() { console.log('--- DEBUG LOAD ORDER DETAIL ---', this.orderId); try {
+>>>>>>> local-backup-root-cyj
const response = await supa
.from('ml_orders')
.select(`
*,
+<<<<<<< HEAD
order_items!inner (
+=======
+ ml_order_items (
+>>>>>>> local-backup-root-cyj
id,
order_id,
product_id,
@@ -289,12 +345,17 @@
.single()
.execute()
+<<<<<<< HEAD
if (response.error != null) {
+=======
+ if (response.error != null || (response.status ?? 200) >= 400) {
+>>>>>>> local-backup-root-cyj
console.error('获取订单详情失败:', response.error)
uni.showToast({ title: '加载失败', icon: 'none' })
return
}
+<<<<<<< HEAD
const rawData = response.data as UTSJSONObject
if (rawData == null) return
@@ -321,11 +382,47 @@
}
const itemsObj = rawData.get('order_items')
+=======
+ console.log('--- DEBUG RAW ORDER DATA ---', response.data); let realData = response.data;
+ let isArrLike = false;
+ if (response.data != null && (response.data as any)['0'] != null) {
+ realData = (response.data as any)['0'];
+ isArrLike = true;
+ }
+ console.log('--- EXTRACTED realData ---', isArrLike);
+ const rawData = realData as UTSJSONObject
+ if (rawData == null) return
+
+ this.order = {
+ id: String(rawData['id'] ?? '') || '',
+ order_no: String(rawData['order_no'] ?? '') || '',
+ user_id: String(rawData['user_id'] ?? '') || '',
+ merchant_id: String(rawData['merchant_id'] ?? '') || '',
+ order_status: Number(rawData['order_status'] ?? 0) || 1,
+ total_amount: Number(rawData['total_amount'] ?? 0) || 0,
+ product_amount: Number(rawData['product_amount'] ?? 0) || 0,
+ shipping_fee: Number(rawData['shipping_fee'] ?? 0) || 0,
+ discount_amount: Number(rawData['discount_amount'] ?? 0) || 0,
+ paid_amount: Number(rawData['paid_amount'] ?? 0) || 0,
+ shipping_address: String(rawData['shipping_address'] ?? '') || '{}',
+ remark: String(rawData['remark'] ?? '') || '',
+ shipping_company: String(rawData['carrier_name'] ?? rawData['shipping_company'] ?? '') || '',
+ tracking_number: String(rawData['tracking_no'] ?? rawData['tracking_number'] ?? '') || '',
+ paid_at: String(rawData['paid_at'] ?? '') || '',
+ shipped_at: String(rawData['shipped_at'] ?? '') || '',
+ created_at: String(rawData['created_at'] ?? '') || '',
+ updated_at: String(rawData['updated_at'] ?? '') || '',
+ items: []
+ }
+
+ const itemsObj = rawData['ml_order_items']
+>>>>>>> local-backup-root-cyj
if (itemsObj != null && Array.isArray(itemsObj)) {
const itemsArray = itemsObj as any[]
for (let i = 0; i < itemsArray.length; i++) {
const orderItem = itemsArray[i] as UTSJSONObject
this.order.items.push({
+<<<<<<< HEAD
id: orderItem.getString('id') || '',
order_id: orderItem.getString('order_id') || '',
product_id: orderItem.getString('product_id') || '',
@@ -335,6 +432,17 @@
price: orderItem.getNumber('price') || 0,
quantity: orderItem.getNumber('quantity') || 0,
image_url: orderItem.getString('image_url') || '',
+=======
+ id: String(orderItem['id'] ?? '') || '',
+ order_id: String(orderItem['order_id'] ?? '') || '',
+ product_id: String(orderItem['product_id'] ?? '') || '',
+ sku_id: String(orderItem['sku_id'] ?? '') || '',
+ product_name: String(orderItem['product_name'] ?? '') || '',
+ sku_name: String(orderItem['sku_name'] ?? '') || '',
+ price: Number(orderItem['price'] ?? 0) || 0,
+ quantity: Number(orderItem['quantity'] ?? 0) || 0,
+ image_url: String(orderItem['image_url'] ?? '') || '',
+>>>>>>> local-backup-root-cyj
sku_snapshot: ''
} as OrderItemType)
}
@@ -442,7 +550,11 @@
},
async confirmShip() {
+<<<<<<< HEAD
if (!this.selectedLogistics.name) {
+=======
+ if (this.selectedLogistics == null || !this.selectedLogistics?.name) {
+>>>>>>> local-backup-root-cyj
uni.showToast({ title: '请选择物流公司', icon: 'none' })
return
}
@@ -456,25 +568,39 @@
.from('ml_orders')
.update({
order_status: 3,
+<<<<<<< HEAD
shipping_company: this.selectedLogistics.name,
tracking_number: this.trackingNumber,
+=======
+ shipping_status: 2,
+ carrier_name: this.selectedLogistics?.name, tracking_no: this.trackingNumber,
+>>>>>>> local-backup-root-cyj
shipped_at: new Date().toISOString(),
updated_at: new Date().toISOString()
})
.eq('id', this.order.id)
.execute()
+<<<<<<< HEAD
if (response.error != null) {
uni.showToast({ title: '发货失败', icon: 'none' })
+=======
+ if (response.error != null || (response.status ?? 200) >= 400) {
+ let msg = response.error?.message ?? (response.data != null ? JSON.stringify(response.data) : '请检查网络或登录状态'); uni.showToast({ title: '发货被拦截: ' + msg, icon: 'none', duration: 4500 }); console.error('SUPABASE API ERR:', response)
+>>>>>>> local-backup-root-cyj
return
}
uni.showToast({ title: '发货成功', icon: 'success' })
this.closeShipModal()
this.loadOrderDetail()
+<<<<<<< HEAD
} catch (e) {
uni.showToast({ title: '发货失败', icon: 'none' })
}
+=======
+ } catch (e) { uni.showToast({ title: '发货发生异常', icon: 'none' }); console.error(e) }
+>>>>>>> local-backup-root-cyj
},
viewLogistics() {
@@ -501,7 +627,11 @@
.eq('id', this.order.id)
.execute()
+<<<<<<< HEAD
if (response.error != null) {
+=======
+ if (response.error != null || (response.status ?? 200) >= 400) {
+>>>>>>> local-backup-root-cyj
uni.showToast({ title: '操作失败', icon: 'none' })
return
}
@@ -529,7 +659,11 @@
.eq('id', this.order.id)
.execute()
+<<<<<<< HEAD
if (response.error != null) {
+=======
+ if (response.error != null || (response.status ?? 200) >= 400) {
+>>>>>>> local-backup-root-cyj
uni.showToast({ title: '删除失败', icon: 'none' })
return
}
@@ -841,7 +975,11 @@
display: flex;
align-items: flex-end;
justify-content: center;
+<<<<<<< HEAD
z-index: 1000;
+=======
+ z-index: 99;
+>>>>>>> local-backup-root-cyj
}
.modal-content {
@@ -849,6 +987,11 @@
background-color: #fff;
border-radius: 24rpx 24rpx 0 0;
padding-bottom: env(safe-area-inset-bottom);
+<<<<<<< HEAD
+=======
+ position: relative;
+ z-index: 99;
+>>>>>>> local-backup-root-cyj
}
.modal-header {
@@ -923,3 +1066,23 @@
font-weight: bold;
}
+<<<<<<< HEAD
+=======
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+>>>>>>> local-backup-root-cyj
diff --git a/pages/mall/merchant/orders.uvue b/pages/mall/merchant/orders.uvue
index 94600c26..d6bdcbf4 100644
--- a/pages/mall/merchant/orders.uvue
+++ b/pages/mall/merchant/orders.uvue
@@ -1,4 +1,8 @@
+<<<<<<< HEAD
+=======
+
+>>>>>>> local-backup-root-cyj
@@ -96,14 +100,22 @@
>>>>>> local-backup-root-cyj
class="action-btn primary"
@click.stop="shipOrder(order)"
>
发货
>>>>>> local-backup-root-cyj
class="action-btn info"
@click.stop="viewLogistics(order)"
>
@@ -147,7 +159,11 @@
@change="onLogisticsChange"
>
+<<<<<<< HEAD
{{ selectedLogistics.name || '请选择物流公司' }}
+=======
+ {{ selectedLogistics?.name || '请选择物流公司' }}
+>>>>>>> local-backup-root-cyj
@@ -222,7 +238,11 @@
{ name: '待发货', status: 2, count: 0 },
{ name: '待收货', status: 3, count: 0 },
{ name: '已完成', status: 4, count: 0 },
+<<<<<<< HEAD
{ name: '退款', status: 0, count: 0 }
+=======
+ { name: '退款', status: 6, count: 0 }
+>>>>>>> local-backup-root-cyj
] as TabType[],
currentTab: -2,
searchKeyword: '',
@@ -257,7 +277,11 @@
const statusMap: Record = {
'pending': 1,
'shipped': 3,
+<<<<<<< HEAD
'refund': 0,
+=======
+ 'refund': 6,
+>>>>>>> local-backup-root-cyj
'completed': 4
}
this.currentTab = statusMap[type] ?? -2
@@ -301,7 +325,11 @@
.from('ml_orders')
.select(`
*,
+<<<<<<< HEAD
order_items!inner (
+=======
+ order_items (
+>>>>>>> local-backup-root-cyj
id,
order_id,
product_id,
@@ -320,8 +348,14 @@
.limit(this.limit)
if (this.currentTab !== -2) {
+<<<<<<< HEAD
if (this.currentTab === 0) {
query = query.eq('order_status', 0)
+=======
+ if (this.currentTab === 6) {
+ // 退款状态同时查询 0 和 6
+ query = query.in('order_status', [0, 6])
+>>>>>>> local-backup-root-cyj
} else {
query = query.eq('order_status', this.currentTab)
}
@@ -333,7 +367,11 @@
const response = await query.execute()
+<<<<<<< HEAD
if (response.error != null) {
+=======
+ if (response.error != null || (response.status ?? 200) >= 400) {
+>>>>>>> local-backup-root-cyj
console.error('获取订单失败:', response.error)
uni.showToast({ title: '加载失败', icon: 'none' })
return
@@ -349,19 +387,32 @@
const ordersData: OrderType[] = []
for (let i = 0; i < rawData.length; i++) {
const item = rawData[i]
+<<<<<<< HEAD
const orderObj = item as UTSJSONObject
+=======
+ const str = JSON.stringify(item)
+ const orderObj = JSON.parse(str) as UTSJSONObject
+>>>>>>> local-backup-root-cyj
const order: OrderType = {
id: orderObj.getString('id') || '',
order_no: orderObj.getString('order_no') || '',
user_id: orderObj.getString('user_id') || '',
merchant_id: orderObj.getString('merchant_id') || '',
+<<<<<<< HEAD
order_status: orderObj.getNumber('order_status') || 1,
+=======
+ order_status: orderObj.getNumber('order_status') ?? (orderObj.get('order_status') == null ? 1 : (orderObj.get('order_status') as number)),
+>>>>>>> local-backup-root-cyj
total_amount: orderObj.getNumber('total_amount') || 0,
product_amount: orderObj.getNumber('product_amount') || 0,
shipping_fee: orderObj.getNumber('shipping_fee') || 0,
paid_amount: orderObj.getNumber('paid_amount') || 0,
+<<<<<<< HEAD
shipping_address: orderObj.getString('shipping_address') || '',
+=======
+ shipping_address: orderObj.get('shipping_address') != null ? (typeof orderObj.get('shipping_address') === 'string' ? orderObj.getString('shipping_address')! : JSON.stringify(orderObj.get('shipping_address'))) : '',
+>>>>>>> local-backup-root-cyj
remark: orderObj.getString('remark') || '',
created_at: orderObj.getString('created_at') || '',
updated_at: orderObj.getString('updated_at') || '',
@@ -372,7 +423,14 @@
if (itemsObj != null && Array.isArray(itemsObj)) {
const itemsArray = itemsObj as any[]
for (let j = 0; j < itemsArray.length; j++) {
+<<<<<<< HEAD
const orderItem = itemsArray[j] as UTSJSONObject
+=======
+ const rawItem = itemsArray[j]
+ const itemStr = JSON.stringify(rawItem)
+ const orderItem = JSON.parse(itemStr) as UTSJSONObject
+
+>>>>>>> local-backup-root-cyj
order.items.push({
id: orderItem.getString('id') || '',
order_id: orderItem.getString('order_id') || '',
@@ -424,13 +482,29 @@
const rawData = response.data as any[]
if (rawData != null) {
for (let i = 0; i < rawData.length; i++) {
+<<<<<<< HEAD
const item = rawData[i] as UTSJSONObject
const status = item.getNumber('order_status') || 1
+=======
+ const row = rawData[i]
+ const istr = JSON.stringify(row)
+ const item = JSON.parse(istr) as UTSJSONObject
+ const status_val = item.get('order_status')
+ let status = 1
+ if (status_val != null) {
+ status = (typeof status_val === 'number') ? (status_val as number) : parseInt(status_val.toString())
+ }
+
+>>>>>>> local-backup-root-cyj
if (status === 1) counts[1]++
else if (status === 2) counts[2]++
else if (status === 3) counts[3]++
else if (status === 4) counts[4]++
+<<<<<<< HEAD
else if (status === 0) counts[0]++
+=======
+ else if (status === 0 || status === 6) counts[0]++
+>>>>>>> local-backup-root-cyj
total++
}
}
@@ -500,7 +574,11 @@
},
async confirmShip() {
+<<<<<<< HEAD
if (!this.selectedLogistics.name) {
+=======
+ if (this.selectedLogistics == null || !this.selectedLogistics?.name) {
+>>>>>>> local-backup-root-cyj
uni.showToast({ title: '请选择物流公司', icon: 'none' })
return
}
@@ -510,6 +588,7 @@
}
try {
+<<<<<<< HEAD
const response = await supa
.from('ml_orders')
.update({
@@ -524,6 +603,31 @@
if (response.error != null) {
uni.showToast({ title: '发货失败', icon: 'none' })
+=======
+ const payloadStr = JSON.stringify({
+ order_status: 3,
+ shipping_status: 2,
+ carrier_name: this.selectedLogistics?.name ?? '未知',
+ tracking_no: this.trackingNumber,
+ shipped_at: new Date().toISOString(),
+ updated_at: new Date().toISOString()
+ });
+ const payload = JSON.parse(payloadStr) as UTSJSONObject;
+ console.log('--- PAYLOAD TO SEND ---', JSON.stringify(payload));
+ const response = await supa.from('ml_orders').update(payload
+ )
+ .eq('id', this.currentOrder!.id)
+ .execute()
+
+ if (response.error != null || (response.status ?? 200) >= 400) {
+ let msg = '';
+ if (response.error != null) msg = response.error!.message;
+ else if (response.data != null) {
+ const rData = response.data as UTSJSONObject;
+ msg = rData.getString('message') ?? rData.getString('code') ?? JSON.stringify(rData);
+ }
+ if (!msg) msg = '请检查网络或登录状态'; uni.showToast({ title: '发货被拦截: ' + msg, icon: 'none', duration: 4500 }); console.error('SUPABASE API ERR:', response)
+>>>>>>> local-backup-root-cyj
return
}
@@ -531,14 +635,22 @@
this.closeShipModal()
this.loadOrders()
this.loadOrderCounts()
+<<<<<<< HEAD
} catch (e) {
uni.showToast({ title: '发货失败', icon: 'none' })
}
+=======
+ } catch (e) { uni.showToast({ title: '发货发生异常', icon: 'none' }); console.error(e) }
+>>>>>>> local-backup-root-cyj
},
viewLogistics(order: OrderType) {
uni.navigateTo({
+<<<<<<< HEAD
url: `/pages/mall/merchant/logistics?orderId=${order.id}`
+=======
+ url: `/pages/mall/consumer/logistics?orderId=${order.id}`
+>>>>>>> local-backup-root-cyj
})
},
@@ -555,7 +667,11 @@
.eq('id', order.id)
.execute()
+<<<<<<< HEAD
if (response.error != null) {
+=======
+ if (response.error != null || (response.status ?? 200) >= 400) {
+>>>>>>> local-backup-root-cyj
uni.showToast({ title: '删除失败', icon: 'none' })
return
}
@@ -576,7 +692,12 @@
if (status === 2) return '待发货'
if (status === 3) return '待收货'
if (status === 4) return '已完成'
+<<<<<<< HEAD
if (status === 0) return '退款中'
+=======
+ if (status === 0 || status === 6) return '退款/售后'
+ if (status === 7) return '退货完成'
+>>>>>>> local-backup-root-cyj
if (status === 5 || status === -1) return '已取消'
return '未知'
},
@@ -717,8 +838,14 @@
.order-card {
background-color: #fff;
+<<<<<<< HEAD
border-radius: 16rpx;
margin-bottom: 20rpx;
+=======
+ border-radius: 20rpx;
+ margin-bottom: 24rpx;
+ box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.04);
+>>>>>>> local-backup-root-cyj
overflow: hidden;
}
@@ -817,22 +944,54 @@
-webkit-box-orient: vertical;
}
+<<<<<<< HEAD
.product-spec {
font-size: 22rpx;
color: #999;
margin-top: 8rpx;
+=======
+ .product-name {
+ font-size: 26rpx;
+ color: #333;
+ font-weight: 500;
+ line-height: 1.4;
+ margin-bottom: 8rpx;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ display: -webkit-box;
+ -webkit-line-clamp: 2;
+ -webkit-box-orient: vertical;
+ }
+
+ .product-spec {
+ font-size: 22rpx;
+ color: #999;
+ background-color: #f8f8f8;
+ padding: 4rpx 12rpx;
+ border-radius: 4rpx;
+ align-self: flex-start;
+>>>>>>> local-backup-root-cyj
}
.product-right {
display: flex;
flex-direction: column;
align-items: flex-end;
+<<<<<<< HEAD
+=======
+ margin-left: 20rpx;
+ min-width: 120rpx;
+>>>>>>> local-backup-root-cyj
}
.product-price {
font-size: 26rpx;
color: #333;
+<<<<<<< HEAD
font-weight: 500;
+=======
+ font-weight: bold;
+>>>>>>> local-backup-root-cyj
}
.product-quantity {
@@ -845,8 +1004,14 @@
display: flex;
justify-content: space-between;
align-items: center;
+<<<<<<< HEAD
padding: 20rpx 24rpx;
border-top: 1rpx solid #f5f5f5;
+=======
+ padding: 24rpx;
+ border-top: 1rpx solid #f8f8f8;
+ background-color: #fafafa;
+>>>>>>> local-backup-root-cyj
}
.order-amount {
@@ -860,7 +1025,11 @@
}
.amount-value {
+<<<<<<< HEAD
font-size: 28rpx;
+=======
+ font-size: 32rpx;
+>>>>>>> local-backup-root-cyj
color: #FF3B30;
font-weight: bold;
margin-left: 10rpx;
@@ -872,14 +1041,27 @@
}
.action-btn {
+<<<<<<< HEAD
padding: 12rpx 24rpx;
font-size: 24rpx;
border-radius: 28rpx;
+=======
+ min-width: 120rpx;
+ height: 56rpx;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 24rpx;
+ border-radius: 28rpx;
+ border: 1rpx solid #eee;
+ padding: 0 20rpx;
+>>>>>>> local-backup-root-cyj
}
.action-btn.primary {
background-color: #007AFF;
color: #fff;
+<<<<<<< HEAD
}
.action-btn.info {
@@ -890,6 +1072,21 @@
.action-btn.default {
background-color: #F5F5F5;
color: #666;
+=======
+ border: none;
+ }
+
+ .action-btn.info {
+ background-color: #fff;
+ color: #007AFF;
+ border-color: #007AFF;
+ }
+
+ .action-btn.default {
+ background-color: #fff;
+ color: #666;
+ border-color: #ddd;
+>>>>>>> local-backup-root-cyj
}
.load-more, .no-more {
@@ -912,7 +1109,11 @@
display: flex;
align-items: flex-end;
justify-content: center;
+<<<<<<< HEAD
z-index: 1000;
+=======
+ z-index: 99;
+>>>>>>> local-backup-root-cyj
}
.modal-content {
@@ -920,6 +1121,11 @@
background-color: #fff;
border-radius: 24rpx 24rpx 0 0;
padding-bottom: env(safe-area-inset-bottom);
+<<<<<<< HEAD
+=======
+ position: relative;
+ z-index: 99;
+>>>>>>> local-backup-root-cyj
}
.modal-header {
@@ -994,3 +1200,19 @@
font-weight: bold;
}
+<<<<<<< HEAD
+=======
+
+
+
+
+
+
+
+
+
+
+
+
+
+>>>>>>> local-backup-root-cyj
diff --git a/pages/mall/merchant/product-detail.uvue b/pages/mall/merchant/product-detail.uvue
index 577b0987..9422a2ae 100644
--- a/pages/mall/merchant/product-detail.uvue
+++ b/pages/mall/merchant/product-detail.uvue
@@ -1,4 +1,8 @@
+<<<<<<< HEAD
+=======
+
+>>>>>>> local-backup-root-cyj
@@ -44,8 +48,13 @@
商品状态
+<<<<<<< HEAD
{{ product.status === 1 ? '上架' : '下架' }}
+=======
+
+ {{ product.status === 1 ? '上架' : (product.status === 2 || product.status === 0 ? '下架' : '其他') }}
+>>>>>>> local-backup-root-cyj
@@ -70,7 +79,11 @@
¥{{ sku.price }}
库存: {{ sku.stock }}
+<<<<<<< HEAD
+=======
+
+>>>>>>> local-backup-root-cyj
{{ sku.status === 1 ? '启用' : '禁用' }}
@@ -705,3 +718,7 @@ export default {
color: #fff;
}
+<<<<<<< HEAD
+=======
+
+>>>>>>> local-backup-root-cyj
diff --git a/pages/mall/merchant/product-edit.uvue b/pages/mall/merchant/product-edit.uvue
index 5818dd5a..c0cd7b15 100644
--- a/pages/mall/merchant/product-edit.uvue
+++ b/pages/mall/merchant/product-edit.uvue
@@ -1,4 +1,8 @@
+<<<<<<< HEAD
+=======
+
+>>>>>>> local-backup-root-cyj
@@ -131,8 +135,28 @@
+<<<<<<< HEAD
总库存 *
+=======
+
+ VIP独立折扣
+ { product.is_vip_discount = e.detail.value as boolean }" />
+
+
+
+ VIP折扣率
+
+
+
+
+ 总库存 *
+>>>>>>> local-backup-root-cyj
+<<<<<<< HEAD
+=======
+
+
+ 会员等级价格 (选填)
+ 若不填写则按照商品销售价或默认折扣计算
+
+
+ {{ level.name }}价格
+
+ ¥
+
+
+
+
+
+>>>>>>> local-backup-root-cyj
商品属性
@@ -231,6 +277,17 @@
logo_url: string
}
+<<<<<<< HEAD
+=======
+ type MemberLevelType = {
+ id: string
+ name: string
+ level_rank: number
+ discount_rate: number
+ price: string // 绑定输入框用
+ }
+
+>>>>>>> local-backup-root-cyj
export default {
data() {
return {
@@ -242,6 +299,10 @@
brands: [] as BrandType[],
brandIndex: -1,
selectedBrand: null as BrandType | null,
+<<<<<<< HEAD
+=======
+ memberLevels: [] as MemberLevelType[],
+>>>>>>> local-backup-root-cyj
product: {
name: '',
subtitle: '',
@@ -255,9 +316,17 @@
total_stock: '',
warning_stock: '10',
unit: '件',
+<<<<<<< HEAD
is_hot: false,
is_new: false,
is_featured: false,
+=======
+ is_hot: false,
+ is_new: false,
+ is_featured: false,
+ is_vip_discount: true,
+ vip_discount_rate: '',
+>>>>>>> local-backup-root-cyj
description: ''
},
merchantId: ''
@@ -265,15 +334,52 @@
},
onLoad(options: any) {
+<<<<<<< HEAD
const productId = options.productId as string
if (productId) {
this.productId = productId
this.isEdit = true
this.loadProductDetail(productId)
+=======
+ let productId = ''
+ if (options) {
+ const keys = Object.keys(options as object)
+ for (let i = 0; i < keys.length; i++) {
+ if (keys[i] === 'productId') {
+ productId = String((options as Record)[keys[i]])
+ }
+ }
+ if (!productId && options['productId']) {
+ productId = String(options['productId'])
+ }
+ // 兼容某些平台
+ if (!productId) {
+ try {
+ const optsStr = JSON.stringify(options)
+ const optsObj = JSON.parse(optsStr) as Record
+ if (optsObj['productId']) {
+ productId = String(optsObj['productId'])
+ }
+ } catch(e) {}
+ }
+ }
+
+ if (productId && productId !== '') {
+ this.productId = productId
+ this.isEdit = true
+ uni.setNavigationBarTitle({ title: '编辑商品' })
+ this.loadProductDetail(productId)
+ } else {
+ uni.setNavigationBarTitle({ title: '添加商品' })
+>>>>>>> local-backup-root-cyj
}
this.initMerchantId()
this.loadCategories()
this.loadBrands()
+<<<<<<< HEAD
+=======
+ this.loadMemberLevels()
+>>>>>>> local-backup-root-cyj
},
methods: {
@@ -281,7 +387,11 @@
try {
const session = supa.getSession()
if (session != null && session.user != null) {
+<<<<<<< HEAD
this.merchantId = session.user.getString('id') || ''
+=======
+ this.merchantId = (session.user as any)['id'] != null ? String((session.user as any)['id']) : ''
+>>>>>>> local-backup-root-cyj
}
if (!this.merchantId) {
this.merchantId = uni.getStorageSync('user_id') || ''
@@ -291,6 +401,78 @@
}
},
+<<<<<<< HEAD
+=======
+ async loadMemberLevels() {
+ try {
+ const response = await supa
+ .from('ml_member_levels')
+ .select('*')
+ .eq('is_active', true)
+ .order('level_rank', { ascending: true })
+ .execute()
+
+ if (response.error != null) {
+ console.error('获取会员等级失败:', response.error)
+ return
+ }
+
+ const rawData = response.data as any[]
+ if (rawData == null) return
+
+ this.memberLevels = []
+ for (let i = 0; i < rawData.length; i++) {
+ const item = rawData[i] as any
+ this.memberLevels.push({
+ id: item['id'] != null ? String(item['id']) : '',
+ name: item['name'] != null ? String(item['name']) : '',
+ level_rank: item['level_rank'] != null ? parseInt(String(item['level_rank'])) : 0,
+ discount_rate: item['discount_rate'] != null ? parseFloat(String(item['discount_rate'])) : 1.0,
+ price: ''
+ } as MemberLevelType)
+ }
+
+ // 如果是编辑模式,还需要加载已有的会员价
+ if (this.isEdit) {
+ this.loadMemberPrices()
+ }
+ } catch (e) {
+ console.error('获取会员等级异常:', e)
+ }
+ },
+
+ async loadMemberPrices() {
+ try {
+ const response = await supa
+ .from('ml_product_member_prices')
+ .select('*')
+ .eq('product_id', this.productId)
+ .execute()
+
+ if (response.error != null) {
+ console.error('获取会员价失败:', response.error)
+ return
+ }
+
+ const rawData = response.data as any[]
+ if (rawData == null || rawData.length == 0) return
+
+ for (let i = 0; i < rawData.length; i++) {
+ const item = rawData[i] as any
+ const levelId = String(item['level_id'])
+ const price = String(item['member_price'])
+
+ const index = this.memberLevels.findIndex(lv => lv.id === levelId)
+ if (index >= 0) {
+ this.memberLevels[index].price = price
+ }
+ }
+ } catch (e) {
+ console.error('获取会员价异常:', e)
+ }
+ },
+
+>>>>>>> local-backup-root-cyj
async loadCategories() {
try {
const response = await supa
@@ -309,10 +491,17 @@
if (rawData == null) return
for (let i = 0; i < rawData.length; i++) {
+<<<<<<< HEAD
const item = rawData[i] as UTSJSONObject
this.categories.push({
id: item.getString('id') || '',
name: item.getString('name') || ''
+=======
+ const item = rawData[i] as any
+ this.categories.push({
+ id: item['id'] != null ? String(item['id']) : '',
+ name: item['name'] != null ? String(item['name']) : ''
+>>>>>>> local-backup-root-cyj
} as CategoryType)
}
} catch (e) {
@@ -338,11 +527,19 @@
if (rawData == null) return
for (let i = 0; i < rawData.length; i++) {
+<<<<<<< HEAD
const item = rawData[i] as UTSJSONObject
this.brands.push({
id: item.getString('id') || '',
name: item.getString('name') || '',
logo_url: item.getString('logo_url') || ''
+=======
+ const item = rawData[i] as any
+ this.brands.push({
+ id: item['id'] != null ? String(item['id']) : '',
+ name: item['name'] != null ? String(item['name']) : '',
+ logo_url: item['logo_url'] != null ? String(item['logo_url']) : ''
+>>>>>>> local-backup-root-cyj
} as BrandType)
}
} catch (e) {
@@ -352,6 +549,10 @@
async loadProductDetail(productId: string) {
try {
+<<<<<<< HEAD
+=======
+ uni.showLoading({ title: '加载商品中...' })
+>>>>>>> local-backup-root-cyj
const response = await supa
.from('ml_products')
.select('*')
@@ -359,6 +560,7 @@
.single()
.execute()
+<<<<<<< HEAD
if (response.error != null) {
console.error('获取商品详情失败:', response.error)
return
@@ -386,6 +588,47 @@
description: rawData.getString('description') || ''
}
+=======
+ uni.hideLoading()
+ if (response.error != null) {
+ console.error('获取详情失败:', response.error)
+ uni.showToast({ title: '没有找到该商品', icon: 'none' })
+ return
+ }
+
+ let rawData = response.data as any
+ if (rawData == null) return
+
+ // 防止Supabase某些版本把single()仍返回数组的坑
+ if (Array.isArray(rawData) && rawData.length > 0) {
+ rawData = rawData[0]
+ }
+
+ const getStr = (key: string): string => { try { return rawData[key] != null ? String(rawData[key]) : '' } catch(e){ return '' } }
+ const getBool = (key: string): boolean => { try { return rawData[key] === true || rawData[key] === 'true' } catch(e){ return false } }
+
+ this.product.name = getStr('name')
+ this.product.subtitle = getStr('subtitle')
+ this.product.category_id = getStr('category_id')
+ this.product.brand_id = getStr('brand_id')
+ this.product.main_image_url = getStr('main_image_url')
+ this.product.imageList = this.parseImageUrls(getStr('image_urls'))
+ this.product.base_price = getStr('base_price')
+ this.product.market_price = getStr('market_price')
+ this.product.cost_price = getStr('cost_price')
+ this.product.total_stock = getStr('total_stock')
+ this.product.warning_stock = getStr('warning_stock') || '10'
+ this.product.unit = getStr('unit') || '件'
+ this.product.is_hot = getBool('is_hot')
+ this.product.is_new = getBool('is_new')
+ this.product.is_featured = getBool('is_featured')
+
+ const _isVip = rawData['is_vip_discount']
+ this.product.is_vip_discount = _isVip == null ? true : getBool('is_vip_discount')
+ this.product.vip_discount_rate = getStr('vip_discount_rate')
+ this.product.description = getStr('description')
+
+>>>>>>> local-backup-root-cyj
if (this.product.category_id) {
this.categoryIndex = this.categories.findIndex(c => c.id === this.product.category_id)
if (this.categoryIndex >= 0) {
@@ -400,7 +643,13 @@
}
}
} catch (e) {
+<<<<<<< HEAD
console.error('获取商品详情异常:', e)
+=======
+ uni.hideLoading()
+ console.error('获取商品详情异常:', e)
+ uni.showToast({ title: '加载异常: ' + String(e), icon: 'none', duration: 3000 })
+>>>>>>> local-backup-root-cyj
}
},
@@ -453,6 +702,7 @@
this.product.imageList.splice(index, 1)
},
+<<<<<<< HEAD
async saveProduct() {
if (!this.product.name) {
uni.showToast({ title: '请输入商品名称', icon: 'none' })
@@ -533,6 +783,203 @@
}
}
}
+=======
+ async uploadImageToSupa(localPath: string): Promise {
+ if (localPath.startsWith('http://') || localPath.startsWith('https://')) {
+ return localPath
+ }
+
+ let ext = '.jpg'
+ const dotIndex = localPath.lastIndexOf('.')
+ if (dotIndex > -1) {
+ ext = localPath.substring(dotIndex).toLowerCase()
+ }
+
+ const uuid = Date.now().toString() + '_' + Math.floor(Math.random() * 1000)
+ const remotePath = `products/${this.merchantId}_${uuid}${ext}`
+
+ try {
+ const uploadResult = await supa.storage.from('zhipao').upload(remotePath, localPath, {})
+ if (uploadResult.error != null) {
+ console.error('上传图片失败:', uploadResult.error)
+ return localPath
+ }
+
+ return supa.storage.getPublicUrl('zhipao', remotePath)
+ } catch (e) {
+ console.error('上传图片异常:', e)
+ return localPath
+ }
+ },
+
+ async saveProduct() {
+ if (!this.product.name) {
+ uni.showToast({ title: '请输入商品名称', icon: 'none' })
+ return
+ }
+ if (!this.product.category_id) {
+ uni.showToast({ title: '请选择商品分类', icon: 'none' })
+ return
+ }
+ if (!this.product.base_price) {
+ uni.showToast({ title: '请输入销售价', icon: 'none' })
+ return
+ }
+ if (!this.product.total_stock) {
+ uni.showToast({ title: '请输入总库存', icon: 'none' })
+ return
+ }
+
+ if (this.product.is_vip_discount && this.product.vip_discount_rate !== '') {
+ const rate = parseFloat(this.product.vip_discount_rate)
+ if (isNaN(rate) || rate <= 0 || rate > 1) {
+ uni.showToast({ title: 'VIP折扣率需在0~1之间', icon: 'none' })
+ return
+ }
+ }
+
+ uni.showLoading({ title: '保存中...' })
+
+ try {
+ let finalMainImage = this.product.main_image_url
+ if (finalMainImage != '') {
+ finalMainImage = await this.uploadImageToSupa(finalMainImage)
+ }
+
+ const finalImageList = [] as string[]
+ for (let i = 0; i < this.product.imageList.length; i++) {
+ const img = await this.uploadImageToSupa(this.product.imageList[i])
+ finalImageList.push(img)
+ }
+
+ const imageUrlsStr = JSON.stringify(finalImageList)
+
+ const productData = {
+ merchant_id: this.merchantId,
+ name: this.product.name,
+ subtitle: this.product.subtitle,
+ category_id: this.product.category_id,
+ brand_id: this.product.brand_id || null,
+ main_image_url: finalMainImage,
+ image_urls: imageUrlsStr,
+ base_price: this.product.base_price ? parseFloat(this.product.base_price) : 0,
+ market_price: this.product.market_price ? parseFloat(this.product.market_price) : null,
+ cost_price: this.product.cost_price ? parseFloat(this.product.cost_price) : null,
+ total_stock: parseInt(this.product.total_stock),
+ available_stock: parseInt(this.product.total_stock),
+ is_hot: this.product.is_hot,
+ is_new: this.product.is_new,
+ is_featured: this.product.is_featured,
+ is_vip_discount: this.product.is_vip_discount,
+ vip_discount_rate: this.product.vip_discount_rate ? parseFloat(this.product.vip_discount_rate) : null,
+ description: this.product.description,
+ status: 1,
+ updated_at: new Date().toISOString()
+ } as UTSJSONObject
+
+ let response : any = null
+ if (this.isEdit) {
+ const updateData = {} as UTSJSONObject
+ const keys = UTSJSONObject.keys(productData)
+ for (let i = 0; i < keys.length; i++) {
+ const key = keys[i]
+ if (key != 'status') {
+ updateData[key] = productData[key]
+ }
+ }
+
+ console.log('执行产品更新, ID:', this.productId)
+ const updateResponse = await supa
+ .from('ml_products')
+ .update(updateData)
+ .eq('id', this.productId)
+ .execute()
+
+ if (updateResponse.error != null) {
+ throw new Error('产品更新失败: ' + String(updateResponse.error!.message))
+ }
+ response = updateResponse
+ } else {
+ productData['created_at'] = new Date().toISOString()
+ productData['product_code'] = 'P' + Date.now().toString()
+ console.log('执行新产品插入')
+ const insertResponse = await supa
+ .from('ml_products')
+ .insert(productData)
+ .execute()
+
+ if (insertResponse.error != null) {
+ throw new Error('产品发布失败: ' + String(insertResponse.error!.message))
+ }
+ response = insertResponse
+ }
+
+ // 保存会员价
+ let targetProductId = this.isEdit ? this.productId : ''
+ if (response != null && response.data != null) {
+ const responseData = response.data
+ if (Array.isArray(responseData)) {
+ const dataArr = responseData as any[]
+ if (dataArr.length > 0) {
+ const firstRow = dataArr[0] as UTSJSONObject
+ if (firstRow['id'] != null) {
+ targetProductId = String(firstRow['id'])
+ }
+ }
+ } else if (responseData instanceof UTSJSONObject) {
+ const dataObj = responseData as UTSJSONObject
+ if (dataObj['id'] != null) {
+ targetProductId = String(dataObj['id'])
+ }
+ }
+ }
+
+ console.log('最终目标产品ID:', targetProductId)
+
+ if (targetProductId && targetProductId !== '' && targetProductId !== 'undefined') {
+ // 1. 先删除旧的会员价
+ if (this.isEdit) {
+ console.log('删除旧会员价:', targetProductId)
+ await supa.from('ml_product_member_prices').delete().eq('product_id', targetProductId).execute()
+ }
+
+ // 2. 插入新的会员价
+ for (let i = 0; i < this.memberLevels.length; i++) {
+ const level = this.memberLevels[i]
+ if (level.price && level.price > 0) {
+ const memberPriceData = {
+ product_id: targetProductId,
+ level_id: level.id,
+ member_price: level.price,
+ created_at: new Date().toISOString()
+ } as UTSJSONObject
+
+ const insertRes = await supa
+ .from('ml_product_member_prices')
+ .insert(memberPriceData)
+ .execute()
+
+ if (insertRes.error != null) {
+ console.error('插入会员价失败', insertRes.error)
+ }
+ }
+ }
+ }
+
+ uni.hideLoading()
+ uni.showToast({ title: '保存成功', icon: 'success' })
+ setTimeout(() => {
+ uni.navigateBack()
+ }, 1500)
+ } catch (e) {
+ uni.hideLoading()
+ console.error('保存商品异常:', e)
+ uni.showToast({ title: '保存异常: ' + String(e), icon: 'none' })
+ }
+ }
+ }
+ }
+>>>>>>> local-backup-root-cyj
+<<<<<<< HEAD
+=======
+
+
+
+>>>>>>> local-backup-root-cyj
diff --git a/pages/mall/merchant/products.uvue b/pages/mall/merchant/products.uvue
index e5d0c782..84255029 100644
--- a/pages/mall/merchant/products.uvue
+++ b/pages/mall/merchant/products.uvue
@@ -1,4 +1,8 @@
+<<<<<<< HEAD
+=======
+
+>>>>>>> local-backup-root-cyj
@@ -84,11 +88,20 @@
{{ product.subtitle || '暂无描述' }}
+<<<<<<< HEAD
热
新
荐
+=======
+
+ 热
+ 新
+ 荐
+ VIP
+
+>>>>>>> local-backup-root-cyj
¥{{ product.base_price }}
@@ -222,7 +235,11 @@
if (this.currentFilter === 'onsale') {
query = query.eq('status', 1)
} else if (this.currentFilter === 'offsale') {
+<<<<<<< HEAD
query = query.eq('status', 0)
+=======
+ query = query.eq('status', 2)
+>>>>>>> local-backup-root-cyj
} else if (this.currentFilter === 'low_stock') {
query = query.lte('total_stock', this.lowStockThreshold).gte('total_stock', 0)
}
@@ -259,7 +276,11 @@
market_price: prodObj.getNumber('market_price') || 0,
total_stock: prodObj.getNumber('total_stock') || 0,
sale_count: prodObj.getNumber('sale_count') || 0,
+<<<<<<< HEAD
status: prodObj.getNumber('status') || 0,
+=======
+ status: prodObj.getNumber('status') || 1,
+>>>>>>> local-backup-root-cyj
is_hot: prodObj.getBoolean('is_hot') || false,
is_new: prodObj.getBoolean('is_new') || false,
is_featured: prodObj.getBoolean('is_featured') || false,
@@ -332,7 +353,11 @@
},
async toggleStatus(product: ProductType) {
+<<<<<<< HEAD
const newStatus = product.status === 1 ? 0 : 1
+=======
+ const newStatus = product.status === 1 ? 2 : 1
+>>>>>>> local-backup-root-cyj
const actionText = newStatus === 1 ? '上架' : '下架'
uni.showModal({
@@ -395,13 +420,21 @@
getStatusClass(status: number): string {
if (status === 1) return 'status-onsale'
+<<<<<<< HEAD
if (status === 0) return 'status-offsale'
+=======
+ if (status === 2 || status === 0) return 'status-offsale'
+>>>>>>> local-backup-root-cyj
return 'status-pending'
},
getStatusText(status: number): string {
if (status === 1) return '在售'
+<<<<<<< HEAD
if (status === 0) return '已下架'
+=======
+ if (status === 2 || status === 0) return '已下架'
+>>>>>>> local-backup-root-cyj
return '待审核'
}
}
@@ -601,10 +634,23 @@
color: #fff;
}
+<<<<<<< HEAD
.tag.recommend {
background-color: #9C27B0;
color: #fff;
}
+=======
+ .tag.recommend {
+ background-color: #9C27B0;
+ color: #fff;
+ }
+
+ .tag.vip {
+ background-color: #FFC107;
+ color: #333;
+ font-weight: bold;
+ }
+>>>>>>> local-backup-root-cyj
.product-stats {
display: flex;
@@ -710,3 +756,9 @@
font-weight: bold;
}
+<<<<<<< HEAD
+=======
+
+
+
+>>>>>>> local-backup-root-cyj
diff --git a/pages/mall/merchant/shop-edit.uvue b/pages/mall/merchant/shop-edit.uvue
index 7f8698f5..2055109a 100644
--- a/pages/mall/merchant/shop-edit.uvue
+++ b/pages/mall/merchant/shop-edit.uvue
@@ -135,13 +135,52 @@
}
})
},
+<<<<<<< HEAD
+=======
+
+ async uploadImageToSupa(localPath: string): Promise {
+ if (localPath.startsWith('http://') || localPath.startsWith('https://')) {
+ return localPath
+ }
+
+ let ext = '.jpg'
+ const dotIndex = localPath.lastIndexOf('.')
+ if (dotIndex > -1) {
+ ext = localPath.substring(dotIndex).toLowerCase()
+ }
+
+ const uuid = Date.now().toString() + '_' + Math.floor(Math.random() * 1000)
+ const remotePath = `shops/${this.merchantId}_${uuid}${ext}`
+
+ try {
+ const uploadResult = await supa.storage.from('zhipao').upload(remotePath, localPath, {})
+ if (uploadResult.status == 200 || uploadResult.status == 201) {
+ const data = uploadResult.data
+ if (data != null) {
+ const dataObj = data as UTSJSONObject
+ const key = dataObj.getString('Key')
+ if (key != null && key != '') {
+ return `${supa.baseUrl}/storage/v1/object/public/${key}`
+ }
+ }
+ }
+ console.error('上传图片失败:', uploadResult.error)
+ return localPath
+ } catch (e) {
+ console.error('上传图片异常:', e)
+ return localPath
+ }
+ },
+
+>>>>>>> local-backup-root-cyj
async saveShop() {
if (!this.shop.shop_name) {
uni.showToast({ title: '请输入店铺名称', icon: 'none' })
return
}
+<<<<<<< HEAD
uni.showLoading({ title: '保存中...' })
try {
@@ -149,6 +188,27 @@
shop_name: this.shop.shop_name,
shop_logo: this.shop.shop_logo,
shop_banner: this.shop.shop_banner,
+=======
+ uni.showLoading({ title: '正在上传图片...' })
+
+ try {
+ let finalLogo = this.shop.shop_logo
+ if (finalLogo != '' && !finalLogo.startsWith('http')) {
+ finalLogo = await this.uploadImageToSupa(finalLogo)
+ }
+
+ let finalBanner = this.shop.shop_banner
+ if (finalBanner != '' && !finalBanner.startsWith('http')) {
+ finalBanner = await this.uploadImageToSupa(finalBanner)
+ }
+
+ uni.showLoading({ title: '保存中...' })
+
+ const shopData = {
+ shop_name: this.shop.shop_name,
+ shop_logo: finalLogo,
+ shop_banner: finalBanner,
+>>>>>>> local-backup-root-cyj
description: this.shop.description,
contact_name: this.shop.contact_name,
contact_phone: this.shop.contact_phone,