From cceb556c62d2bb1287178eb0bd1e932776ed1476 Mon Sep 17 00:00:00 2001 From: cyh666666 Date: Tue, 3 Mar 2026 17:02:53 +0800 Subject: [PATCH] =?UTF-8?q?consumer=E6=A8=A1=E5=9D=97=E5=AE=8C=E6=88=90?= =?UTF-8?q?=E5=BA=A695%=EF=BC=8C=E5=AE=89=E5=8D=93=E7=AB=AF=E5=A4=A7?= =?UTF-8?q?=E9=83=A8=E5=88=86=E9=A1=B5=E9=9D=A2=E8=83=BD=E6=AD=A3=E5=B8=B8?= =?UTF-8?q?=E8=8E=B7=E5=8F=96=E6=95=B0=E6=8D=AE=EF=BC=8C=E9=A1=B5=E9=9D=A2?= =?UTF-8?q?=E6=A0=B7=E5=BC=8F=E6=98=BE=E7=A4=BA=E5=9F=BA=E6=9C=AC=E6=AD=A3?= =?UTF-8?q?=E5=B8=B8=EF=BC=8C=E9=80=90=E6=B8=90=E5=AE=8C=E5=96=84=EF=BC=9B?= =?UTF-8?q?=E6=B6=88=E8=B4=B9=E8=80=85=E7=AB=AF=E7=9A=84=E7=A7=AF=E5=88=86?= =?UTF-8?q?=E3=80=81=E4=BD=99=E9=A2=9D=E3=80=81=E8=AF=84=E4=BB=B7=E3=80=81?= =?UTF-8?q?=E4=BC=98=E6=83=A0=E5=88=B8=E7=AD=89=E5=B0=8F=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E6=AD=A3=E5=9C=A8=E5=AE=8C=E5=96=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/supadb/aksupa.uts | 19 +- pages/mall/consumer/chat copy.uvue | 783 ++++++++++ pages/mall/consumer/chat.uvue | 242 +++- pages/mall/consumer/doc/UTS_ANDROID_GUIDE.md | 1175 ++++++++++++++- pages/mall/consumer/index copy.uvue | 10 +- pages/mall/consumer/index.uvue | 13 +- pages/mall/consumer/logistics.uvue | 160 ++- pages/mall/consumer/order-detail.uvue | 812 ++++++++--- pages/mall/consumer/orders copy.uvue | 1358 ++++++++++++++++++ pages/mall/consumer/orders.uvue | 613 +++++--- pages/mall/consumer/payment-success.uvue | 16 +- pages/mall/consumer/payment.uvue | 125 +- pages/mall/consumer/profile.uvue | 20 +- pages/user/login.uvue | 4 +- utils/supabaseService.uts | 237 ++- 15 files changed, 4975 insertions(+), 612 deletions(-) create mode 100644 pages/mall/consumer/chat copy.uvue create mode 100644 pages/mall/consumer/orders copy.uvue diff --git a/components/supadb/aksupa.uts b/components/supadb/aksupa.uts index c107e8b8..3254a1c1 100644 --- a/components/supadb/aksupa.uts +++ b/components/supadb/aksupa.uts @@ -364,12 +364,23 @@ export class AkSupaQueryBuilder { } } if (total == 0) { - if (typeof res['count'] == 'number') { - total = res['count'] as number ?? 0; + // 使用 JSON 序列化访问 res 对象 + const resStr = JSON.stringify(res) + const resParsed = JSON.parse(resStr) + if (resParsed != null) { + const resObj = resParsed as UTSJSONObject + const countVal = resObj.getNumber('count') + if (countVal != null) { + total = countVal + } else if (Array.isArray(resdata)) { + total = resdata.length + } else { + total = 0 + } } else if (Array.isArray(resdata)) { - total = resdata.length; + total = resdata.length } else { - total = 0; + total = 0 } } if (!hasmore) hasmore = (page * limit) < total; // 如果是 head 模式,只返回 count 信息 diff --git a/pages/mall/consumer/chat copy.uvue b/pages/mall/consumer/chat copy.uvue new file mode 100644 index 00000000..62f43c61 --- /dev/null +++ b/pages/mall/consumer/chat copy.uvue @@ -0,0 +1,783 @@ + + + + + + diff --git a/pages/mall/consumer/chat.uvue b/pages/mall/consumer/chat.uvue index 6b551cc5..62f43c61 100644 --- a/pages/mall/consumer/chat.uvue +++ b/pages/mall/consumer/chat.uvue @@ -17,10 +17,13 @@ @@ -39,13 +42,13 @@ v-for="message in messages" :key="message.id" :class="['message-item', message.type]" - :id="'msg-' + message.id" + :id="message.viewId" > @@ -67,7 +70,7 @@ @@ -127,6 +130,7 @@ import { getCurrentUser } from '@/utils/store.uts' type UiChatMessage = { id: string + viewId: string type: string content: string time: string @@ -141,19 +145,39 @@ const scrollToView = ref('') const currentUserId = ref('') const merchantId = ref('') // 商家ID const headerTitle = ref('在线客服') +const merchantAvatar = ref('/static/default-shop.png') // 商家头像 const navPaddingTop = ref('30px') // 默认值,包含状态栏高度+原有内边距 +const isInitialLoading = ref(true) let realtimeChannel: AkSupaRealtimeChannel | null = null // 模拟表情列表 const emojiList = ['😊', '😂', '🤣', '😍', '😘', '🥰', '😭', '😡', '👍', '👏', '🙏', '🎉', '❤️', '🔥', '⭐'] -function scrollToBottom(): void { - nextTick(() => { - if (messages.value.length > 0) { - const lastMsgId = messages.value[messages.value.length - 1].id - scrollToView.value = 'msg-' + lastMsgId - } - }) +function scrollToBottom() : void { + if (messages.value.length === 0) return + + // 获取最后一条消息的 ID + const lastMsg = messages.value[messages.value.length - 1] + const targetId = 'msg-' + lastMsg.id + + // 关键点:在 UVue 安卓端,直接连续赋值可能被合并。 + // 我们先清除 ID,然后在下一帧赋值,确保 scroll-view 监听到变化。 + scrollToView.value = '' + + // 增加多次尝试,确保在 DOM 彻底完成渲染(包含由于高度计算引起的多次排版)后定位。 + setTimeout(() => { + scrollToView.value = targetId + console.log('[scrollToBottom] 发起第一次滚动定位:', targetId) + + // 二次校准:针对长消息或图片导致的高度变化 + setTimeout(() => { + scrollToView.value = '' + setTimeout(() => { + scrollToView.value = targetId + console.log('[scrollToBottom] 二次校准完成:', targetId) + }, 50) + }, 100) + }, 300) } function getCurrentTime(): string { @@ -167,14 +191,12 @@ function setupRealtimeSubscription(): void { console.log('开始建立聊天实时订阅...') console.log('当前用户ID:', currentUserId.value, '商家ID:', merchantId.value) - const filter = ({ - event: 'INSERT', - schema: 'public', - table: 'ml_chat_messages' - } as UTSJSONObject) - - realtimeChannel = supa.channel('public:ml_chat_messages') - .on('postgres_changes', filter, (payload: any) => { + realtimeChannel = supa.channel('chat-messages-' + Date.now().toString()) + .on('postgres_changes', { + event: 'INSERT', + schema: 'public', + table: 'ml_chat_messages' + }, (payload: any) => { console.log('=== 收到实时订阅回调 ===') const payloadObj = (payload instanceof UTSJSONObject) ? (payload as UTSJSONObject) : (JSON.parse(JSON.stringify(payload ?? {})) as UTSJSONObject) const newMsgAny = payloadObj.get('new') @@ -228,8 +250,12 @@ function setupRealtimeSubscription(): void { const date = new Date(createdAt) const timeStr = `${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}` + // 生成安全的 viewId + const safeViewId = 'msg_' + msgId.replace(/[^a-zA-Z0-9]/g, '_') + const incomingMsg: UiChatMessage = { id: msgId, + viewId: safeViewId, type: isMyMessage ? 'sent' : 'received', content: content, time: timeStr @@ -253,40 +279,96 @@ function setupRealtimeSubscription(): void { } async function loadChatHistory(): Promise { - let rawMsgs: ChatMessage[] = [] + let rawMsgs : ChatMessage[] = [] - if (merchantId.value != '') { - rawMsgs = await supabaseService.getChatMessages(merchantId.value) - } else { - console.warn("No merchant ID provided for chat") - return - } + if (merchantId.value != '') { + rawMsgs = await supabaseService.getChatMessages(merchantId.value) + } else { + console.warn("No merchant ID provided for chat") + return + } - // 使用 for 循环替代 map - const uiMessages: UiChatMessage[] = [] - for (let i = rawMsgs.length - 1; i >= 0; i--) { - const m = rawMsgs[i] - const date = new Date(m.created_at ?? new Date().toISOString()) - const timeStr = `${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}` + // 确保时间顺序是升序(旧的在前,新的在后) + // Supabase 返回的消息如果是降序,我们需要 reverse 过来显示 + const sortedRawMsgs = rawMsgs.sort((a, b) => { + const timeA = new Date(a.created_at ?? '').getTime() + const timeB = new Date(b.created_at ?? '').getTime() + return timeA - timeB + }) - const sender = m.sender_id ?? '' - const msgType = (currentUserId.value != '' && sender == currentUserId.value) ? 'sent' : 'received' - const rawId = (m.id ?? '').toString() - const msgId = rawId != '' ? rawId : Date.now().toString() + i.toString() + const uiMessages : UiChatMessage[] = [] + for (let i = 0; i < sortedRawMsgs.length; i++) { + const m = sortedRawMsgs[i] + const date = new Date(m.created_at ?? new Date().toISOString()) + const timeStr = `${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}` + + const sender = m.sender_id ?? '' + const msgType = (currentUserId.value != '' && sender == currentUserId.value) ? 'sent' : 'received' + const rawId = (m.id ?? '').toString() + const msgId = rawId != '' ? rawId : Date.now().toString() + i.toString() + const safeViewId = 'msg_' + msgId.replace(/[^a-zA-Z0-9]/g, '_') + + const uiMsg : UiChatMessage = { + id: msgId, + viewId: safeViewId, + type: msgType, + content: m.content ?? '', + time: timeStr + } + uiMessages.push(uiMsg) + } + messages.value = uiMessages + + if (isInitialLoading.value) { + // 增加一点初始化延迟,等待 scroll-view 渲染就绪 + setTimeout(() => { + scrollToBottom() + isInitialLoading.value = false + }, 500) + } +} + +function onScrollToUpper(e: any): void { + console.log('[onScrollToUpper] 触发加载历史记录') +} + +async function loadMerchantInfo(): Promise { + if (merchantId.value == '') return + + try { + const response = await supa + .from('ml_shops') + .select('shop_logo, shop_name') + .eq('merchant_id', merchantId.value) + .limit(1) + .execute() - const uiMsg: UiChatMessage = { - id: msgId, - type: msgType, - content: m.content ?? '', - time: timeStr + if (response.error != null) { + console.error('[loadMerchantInfo] 获取商家信息失败:', response.error) + return } - uiMessages.push(uiMsg) + + const rawData = response.data + if (rawData == null) return + + const rawList = rawData as any[] + if (rawList.length == 0) return + + const shopData = rawList[0] + const shopObj = JSON.parse(JSON.stringify(shopData)) as UTSJSONObject + + const logo = shopObj.getString('shop_logo') + if (logo != null && logo != '') { + merchantAvatar.value = logo + } + + const name = shopObj.getString('shop_name') + if (name != null && name != '' && headerTitle.value == '在线客服') { + headerTitle.value = name + } + } catch (e) { + console.error('[loadMerchantInfo] 获取商家信息异常:', e) } - messages.value = uiMessages - - setTimeout(() => { - scrollToBottom() - }, 100) } // 生命周期 @@ -320,6 +402,7 @@ onMounted(() => { }) } + loadMerchantInfo() loadChatHistory() setupRealtimeSubscription() }) @@ -340,15 +423,16 @@ const sendMessage = async () => { // 发送到 Supabase if (merchantId.value != '') { - // 不使用乐观更新,等待实时订阅推送 - // 这样可以确保多端同步 + console.log('[sendMessage] 开始发送消息到:', merchantId.value) const success = await supabaseService.sendMessage(merchantId.value, content) + console.log('[sendMessage] 发送结果:', success) if (!success) { uni.showToast({ title: '发送失败', icon: 'none' }) } + // 不需要手动添加消息,等待实时订阅推送 } } @@ -438,22 +522,23 @@ const goBack = () => { diff --git a/pages/mall/consumer/orders.uvue b/pages/mall/consumer/orders.uvue index d6781d5a..3e3b2249 100644 --- a/pages/mall/consumer/orders.uvue +++ b/pages/mall/consumer/orders.uvue @@ -18,13 +18,20 @@ - - - + + + 全部 + + + + {{ tab.name }} @@ -58,10 +65,15 @@ v-for="order in orders" :key="order.id" class="order-card" + @click="viewOrderDetail(order.id)" > - - - 订单号:{{ order.order_no }} + + + + 🏪 + {{ order.shop_name != null && order.shop_name != '' ? order.shop_name : '自营店铺' }} + + {{ getStatusText(order.status) }} @@ -81,34 +93,29 @@ mode="aspectFill" /> - {{ product.name }} - {{ product.spec }} + + {{ product.name }} + {{ product.spec }} + ¥{{ product.price }} - ×{{ product.quantity }} + x{{ product.quantity }} - - - - 商品合计 - ¥{{ order.product_amount }} - - - 运费 - ¥{{ order.shipping_fee }} - - - 实付款 - ¥{{ order.total_amount }} + + + {{ formatDate(order.create_time) }} + + 共{{ order.products.length }}件商品 实付: + ¥{{ order.total_amount }} - + @@ -162,24 +169,6 @@ import { ref, reactive, onMounted, computed } from 'vue' import { onShow, onLoad, onBackPress } from '@dcloudio/uni-app' import { supabaseService } from '@/utils/supabaseService.uts' -// 拦截返回事件,避免跳回登录页 -onBackPress((options) => { - if (options.from === 'navigateBack') { - const pages = getCurrentPages() - if (pages.length > 1) { - const prevPage = pages[pages.length - 2] - // 如果上一页是登录页,则重定向到个人中心 - if (prevPage.route.includes('login')) { - uni.redirectTo({ - url: '/pages/mall/consumer/profile' - }) - return true - } - } - } - return false -}) - // 定义标签页类型 type OrderTabItem = { id: string, @@ -206,6 +195,8 @@ type OrderItem = { product_amount: number, shipping_fee: number, total_amount: number, + merchant_id: string, + shop_name: string, products: OrderProduct[] } @@ -230,7 +221,10 @@ const orderTabs = ref([ { id: 'cancelled', name: '已取消', count: 0 } ]) -// Removed Mock Data +// 模拟状态筛选(除去"全部"后的其余标签) +const orderTabsMobile = computed((): OrderTabItem[] => { + return orderTabs.value.filter((tab: OrderTabItem) => tab.id !== 'all') +}) // 辅助函数:获取状态码 @@ -243,59 +237,98 @@ const getStatusByTab = (tabId: string): number => { return 0 } -// 辅助函数:解析规格文本 -const parseSpecText = (specs: any): string => { - if (specs == null) return '' - if (typeof specs === 'string') return specs - // 对于对象类型,尝试转为JSON字符串或简单处理 +// 格式化规格对象为友好的文本 - 必须在 parseSpecText 之前定义 +function formatSpecObj(obj: any): string { + if (obj == null) return '' + if (typeof obj !== 'object') { + // 非对象类型直接返回字符串形式 + if (typeof obj === 'string') return obj + if (typeof obj === 'number') return obj.toString() + return '' + } + try { - return JSON.stringify(specs) + const objStr = JSON.stringify(obj) + const objParsed = JSON.parse(objStr) + if (objParsed == null) return '' + + const specObj = objParsed as UTSJSONObject + + // 使用 JSON.stringify 获取所有键 + const specObjStr = JSON.stringify(specObj) + const specObjForKeys = JSON.parse(specObjStr) as UTSJSONObject + + // 手动提取键值对 + const parts: string[] = [] + + // 尝试获取已知字段 + const colorVal = specObjForKeys.getString('Color') + if (colorVal != null && colorVal != '') { + parts.push('Color: ' + colorVal) + } + + const sizeVal = specObjForKeys.getString('Size') + if (sizeVal != null && sizeVal != '') { + parts.push('Size: ' + sizeVal) + } + + const defaultVal = specObjForKeys.getString('默认') + if (defaultVal != null && defaultVal != '') { + parts.push('默认: ' + defaultVal) + } + + // 如果没有匹配到已知字段,尝试直接显示 JSON + if (parts.length === 0) { + // 尝试遍历对象 + const objAny = specObjForKeys as any + if (objAny != null) { + return specObjStr.replace(/[{}"]/g, '').replace(/:/g, ': ').replace(/,/g, ' | ') + } + } + + return parts.join(' | ') } catch (e) { return '' } } +// 辅助函数:解析规格文本 +function parseSpecText(specs: any): string { + if (specs == null) return '' + if (typeof specs === 'string') { + // 如果是 JSON 字符串,尝试解析 + if (specs.startsWith('{') || specs.startsWith('[')) { + try { + const parsed = JSON.parse(specs) + if (parsed == null) return specs + return formatSpecObj(parsed) + } catch (e) { + return specs + } + } + return specs + } + // 对于对象类型,格式化显示 + return formatSpecObj(specs) +} + // 辅助函数:更新标签计数 -const updateTabsCounts = (allOrders: any[]) => { - // 直接重新赋值整个数组 - const tabsData = orderTabs.value +const updateTabsCounts = (allOrders: OrderItem[]) => { // 计算各状态数量 const countAll = allOrders.length - const countPending = allOrders.filter((o: any) => { - const obj = o as Record - return obj['status'] === 1 - }).length - const countShipping = allOrders.filter((o: any) => { - const obj = o as Record - return obj['status'] === 2 - }).length - const countDelivering = allOrders.filter((o: any) => { - const obj = o as Record - return obj['status'] === 3 - }).length - const countCompleted = allOrders.filter((o: any) => { - const obj = o as Record - return obj['status'] === 4 - }).length - const countCancelled = allOrders.filter((o: any) => { - const obj = o as Record - return obj['status'] === 5 - }).length + const countPending = allOrders.filter((o: OrderItem) => o.status === 1).length + const countShipping = allOrders.filter((o: OrderItem) => o.status === 2).length + const countDelivering = allOrders.filter((o: OrderItem) => o.status === 3).length + const countCompleted = allOrders.filter((o: OrderItem) => o.status === 4).length + const countCancelled = allOrders.filter((o: OrderItem) => o.status === 5).length // 更新数组元素 - const tabsArr = tabsData as any[] - const tab0 = tabsArr[0] as Record - tab0['count'] = countAll - const tab1 = tabsArr[1] as Record - tab1['count'] = countPending - const tab2 = tabsArr[2] as Record - tab2['count'] = countShipping - const tab3 = tabsArr[3] as Record - tab3['count'] = countDelivering - const tab4 = tabsArr[4] as Record - tab4['count'] = countCompleted - const tab5 = tabsArr[5] as Record - tab5['count'] = countCancelled + orderTabs.value[0].count = countAll + orderTabs.value[1].count = countPending + orderTabs.value[2].count = countShipping + orderTabs.value[3].count = countDelivering + orderTabs.value[4].count = countCompleted + orderTabs.value[5].count = countCancelled } // 辅助函数:按标签筛选订单 @@ -317,69 +350,124 @@ const loadOrders = async () => { try { // Fetch all orders from Supabase (status=0) const fetchedOrders = await supabaseService.getOrders(0) + console.log('[loadOrders] 获取到订单数量:', fetchedOrders.length) // Map to View Model - const mappedOrders: any[] = [] + const mappedOrders: OrderItem[] = [] for (let i = 0; i < fetchedOrders.length; i++) { const order = fetchedOrders[i] - const orderObj = order as Record - const items = orderObj['ml_order_items'] as any[] - const productsList: any[] = [] + // 使用 JSON 序列化转换 + const orderStr = JSON.stringify(order) + const orderParsed = JSON.parse(orderStr) + if (orderParsed == null) continue + const orderObj = orderParsed as UTSJSONObject - if (items != null) { - for (let j = 0; j < items.length; j++) { - const item = items[j] - const itemObj = item as Record - const specRaw = itemObj['specifications'] - const specText = specRaw != null ? parseSpecText(specRaw) : '' - productsList.push({ - id: itemObj['product_id'], - name: itemObj['product_name'], - price: itemObj['price'], - image: itemObj['image_url'] ?? '/static/default-product.png', - spec: specText, - quantity: itemObj['quantity'] - }) + const itemsRaw = orderObj.get('ml_order_items') + const productsList: OrderProduct[] = [] + + console.log('[loadOrders] 订单商品数据:', itemsRaw) + + if (itemsRaw != null) { + // 先检查是否为数组 + if (Array.isArray(itemsRaw)) { + const items = itemsRaw as any[] + console.log('[loadOrders] 商品数量:', items.length) + for (let j = 0; j < items.length; j++) { + const item = items[j] + const itemStr = JSON.stringify(item) + const itemParsed = JSON.parse(itemStr) + if (itemParsed == null) continue + const itemObj = itemParsed as UTSJSONObject + + const specRaw = itemObj.get('specifications') + const specText = specRaw != null ? parseSpecText(specRaw) : '' + + const productId = itemObj.getString('product_id') + const productName = itemObj.getString('product_name') + const price = itemObj.getNumber('price') + const imageUrl = itemObj.getString('image_url') + const quantity = itemObj.getNumber('quantity') + + console.log('[loadOrders] 商品:', productName, '图片:', imageUrl, '规格:', specText) + + const productItem: OrderProduct = { + id: productId ?? '', + name: productName ?? '未知商品', + price: price ?? 0, + image: imageUrl ?? '/static/default-product.png', + spec: specText, + quantity: quantity ?? 1 + } + productsList.push(productItem) + } } } - mappedOrders.push({ - id: orderObj['id'], - order_no: orderObj['order_no'], - status: orderObj['order_status'], - create_time: orderObj['created_at'], - product_amount: orderObj['product_amount'] ?? 0, - shipping_fee: orderObj['shipping_fee'] ?? 0, - total_amount: orderObj['total_amount'] ?? orderObj['paid_amount'] ?? 0, + const orderId = orderObj.getString('id') + const orderNo = orderObj.getString('order_no') + const orderStatus = orderObj.getNumber('order_status') + const createdAt = orderObj.getString('created_at') + const productAmount = orderObj.getNumber('product_amount') + const shippingFee = orderObj.getNumber('shipping_fee') + const totalAmount = orderObj.getNumber('total_amount') + const paidAmount = orderObj.getNumber('paid_amount') + const merchantId = orderObj.getString('merchant_id') + + // 从关联查询的 ml_shops 表获取店铺名称 + let shopName = '自营店铺' + const shopsRaw = orderObj.get('ml_shops') + if (shopsRaw != null) { + const shopStr = JSON.stringify(shopsRaw) + const shopParsed = JSON.parse(shopStr) + if (shopParsed != null) { + const shopObj = shopParsed as UTSJSONObject + const shopNameFromDb = shopObj.getString('shop_name') + if (shopNameFromDb != null && shopNameFromDb != '') { + shopName = shopNameFromDb + } + } + } else if (merchantId != null && merchantId != '') { + shopName = '商家店铺' + } + + console.log('[loadOrders] 订单号:', orderNo, '店铺:', shopName, '商品数:', productsList.length) + + // 如果没有商品数据,添加一个占位商品 + if (productsList.length === 0) { + const placeholderProduct: OrderProduct = { + id: 'placeholder', + name: '订单商品', + price: totalAmount ?? paidAmount ?? 0, + image: '/static/default-product.png', + spec: '', + quantity: 1 + } + productsList.push(placeholderProduct) + } + + const mappedOrder: OrderItem = { + id: orderId ?? '', + order_no: orderNo ?? '', + status: orderStatus ?? 1, + create_time: createdAt ?? '', + product_amount: productAmount ?? 0, + shipping_fee: shippingFee ?? 0, + total_amount: totalAmount ?? paidAmount ?? 0, + merchant_id: merchantId ?? '', + shop_name: shopName, products: productsList - }) + } + mappedOrders.push(mappedOrder) } - // Sort by created_at desc - mappedOrders.sort((a: any, b: any) => { - const aObj = a as Record - const bObj = b as Record - const timeA = new Date(aObj['create_time'] as string).getTime() - const timeB = new Date(bObj['create_time'] as string).getTime() + // Sort by created_at desc - 直接使用 OrderItem 类型访问属性 + mappedOrders.sort((a: OrderItem, b: OrderItem) => { + const timeA = new Date(a.create_time).getTime() + const timeB = new Date(b.create_time).getTime() return timeB - timeA }) - // 将 mappedOrders 转换为 OrderItem[] 类型 - const typedOrders: OrderItem[] = [] - for (let i = 0; i < mappedOrders.length; i++) { - const mo = mappedOrders[i] as Record - typedOrders.push({ - id: mo['id'] as string, - order_no: mo['order_no'] as string, - status: mo['status'] as number, - create_time: mo['create_time'] as string, - product_amount: mo['product_amount'] as number, - shipping_fee: mo['shipping_fee'] as number, - total_amount: mo['total_amount'] as number, - products: mo['products'] as OrderProduct[] - }) - } - allOrdersList.value = typedOrders + allOrdersList.value = mappedOrders // Update tab counts updateTabsCounts(mappedOrders) @@ -397,14 +485,17 @@ const loadOrders = async () => { // 生命周期 onLoad((options) => { - if (options['status'] != null) { - const status = options['status'] as string + if (options == null) return + const statusVal = options['status'] + if (statusVal != null) { + const status = statusVal as string if (['all', 'pending', 'shipping', 'delivering', 'completed', 'cancelled'].includes(status)) { activeTab.value = status } } - if (options['type'] != null) { - const type = options['type'] as string + const typeVal = options['type'] + if (typeVal != null) { + const type = typeVal as string if (type === 'pending') activeTab.value = 'pending' else if (type === 'shipped') activeTab.value = 'delivering' // 映射到待收货 else if (type === 'review') activeTab.value = 'completed' // 映射到已完成 @@ -500,6 +591,8 @@ const getStatusText = (status: number): string => { if (status == 3) return '待收货' if (status == 4) return '已完成' if (status == 5) return '已取消' + if (status == 6) return '退款中' + if (status == 7) return '已退款' return '未知状态' } @@ -510,6 +603,8 @@ const getStatusClass = (status: number): string => { if (status == 3) return 'status-delivering' if (status == 4) return 'status-completed' if (status == 5) return 'status-cancelled' + if (status == 6) return 'status-refunding' + if (status == 7) return 'status-refunded' return 'status-unknown' } @@ -705,10 +800,12 @@ const goShopping = () => {