完成订单管理的数据库链接和展示

This commit is contained in:
2026-03-16 10:40:33 +08:00
parent 500160df11
commit 05aba1fa4f
2 changed files with 316 additions and 65 deletions

View File

@@ -1,6 +1,6 @@
<template>
<view v-if="visible" class="drawer-mask" @click="close">
<view class="drawer-container" @click.stop>
<view v-if="visible" class="drawer-mask" :class="{ closing: isClosing }" @click="close">
<view class="drawer-container" :class="{ closing: isClosing }" @click.stop>
<view class="drawer-header">
<view class="header-left">
<text class="title">订单详情</text>
@@ -36,12 +36,12 @@
<text class="value status-val">¥ {{ orderInfo['paidAmount'] }}</text>
</view>
<view class="summary-grid">
<text class="label">支付方式</text>
<text class="label">支付状态</text>
<text class="value">{{ orderInfo['payMethod'] }}</text>
</view>
<view class="summary-grid">
<text class="label">配送人员</text>
<text class="value">{{ orderInfo['delivery_name'] }}</text>
<text class="label">支付时间</text>
<text class="value">{{ orderInfo['payTime'] || '--' }}</text>
</view>
</view>
</view>
@@ -108,7 +108,7 @@
</view>
<view class="info-item">
<text class="label">商品总数:</text>
<text class="value">{{ orderInfo['total_num'] || '0' }}</text>
<text class="value">{{ totalNum > 0 ? totalNum : (orderInfo['total_num'] || '0') }}</text>
</view>
<view class="info-item">
<text class="label">产品金额:</text>
@@ -179,7 +179,7 @@
</view>
<view class="p-td p-price">¥{{ p['price'] }}</view>
<view class="p-td p-num">{{ p['quantity'] }}</view>
<view class="p-td p-total">¥{{ (parseFloat(p['price'] as string) * parseInt(p['quantity'] as string)).toFixed(2) }}</view>
<view class="p-td p-total">¥{{ ((p['price'] as number) * (p['quantity'] as number)).toFixed(2) }}</view>
</view>
</view>
</view>
@@ -187,13 +187,16 @@
<!-- 订单记录 -->
<view v-if="activeTab === 2" class="info-section">
<view class="timeline">
<view v-if="logs.length === 0" class="empty-logs">
<text class="empty-logs-text">暂无订单记录</text>
</view>
<view v-else class="timeline">
<view v-for="(log, li) in logs" :key="li" class="timeline-item">
<view class="dot"></view>
<view class="line" v-if="li !== logs.length - 1"></view>
<view class="log-content">
<text class="log-title">{{ log.title }}</text>
<text class="log-time">{{ log.time }}</text>
<text class="log-title">{{ log['title'] }}</text>
<text class="log-time">{{ log['time'] }}</text>
</view>
</view>
</view>
@@ -206,6 +209,7 @@
<script setup lang="uts">
import { ref, computed, watch } from 'vue'
import { supabase } from '@/components/supadb/aksupainstance.uts'
const props = defineProps({
visible: { type: Boolean, default: false },
@@ -217,22 +221,97 @@ const emit = defineEmits(['update:visible'])
const activeTab = ref(0)
const tabs = ['订单信息', '商品信息', '订单记录']
const productItems = computed<UTSJSONObject[]>(() => {
return (props.orderInfo['items'] || []) as UTSJSONObject[]
const fetchedItems = ref<UTSJSONObject[]>([])
const itemsLoading = ref(false)
const isClosing = ref(false)
const productItems = computed<UTSJSONObject[]>(() => fetchedItems.value)
const totalNum = computed<number>(() => {
return fetchedItems.value.reduce((sum: number, p: UTSJSONObject) : number => {
const qty = p['quantity'] != null ? (p['quantity'] as number) : 0
return sum + qty
}, 0)
})
const logs = ref([
{ title: '订单生成', time: '2026-02-27 15:47:25' },
{ title: '支付成功', time: '2026-02-27 15:48:30' }
])
const fmtTime = (timeStr : string | null) : string => {
if (timeStr == null || timeStr == '') return ''
if (timeStr.indexOf('T') > -1) {
const parts = timeStr.split('T')
const timePart = parts[1].split('.')[0]
return parts[0] + ' ' + timePart
}
return timeStr
}
const logs = computed<UTSJSONObject[]>(() => {
const result: UTSJSONObject[] = []
const createdAt = props.orderInfo['created_at'] as string | null
if (createdAt != null && createdAt !== '' && createdAt !== '--') {
result.push({ title: '订单创建', time: createdAt } as UTSJSONObject)
}
const paidAt = props.orderInfo['payTime'] as string | null
if (paidAt != null && paidAt !== '' && paidAt !== '--') {
result.push({ title: '支付成功', time: paidAt } as UTSJSONObject)
}
const shippedAt = fmtTime(props.orderInfo['shipped_at'] as string | null)
if (shippedAt !== '') {
result.push({ title: '商家发货', time: shippedAt } as UTSJSONObject)
}
const deliveredAt = fmtTime(props.orderInfo['delivered_at'] as string | null)
if (deliveredAt !== '') {
result.push({ title: '确认收货', time: deliveredAt } as UTSJSONObject)
}
const completedAt = fmtTime(props.orderInfo['completed_at'] as string | null)
if (completedAt !== '') {
result.push({ title: '交易完成', time: completedAt } as UTSJSONObject)
}
return result
})
const fetchItems = async () => {
const orderId = props.orderInfo['id']
if (orderId == null || orderId === '') return
itemsLoading.value = true
fetchedItems.value = []
try {
const res = await supabase
.from('ml_order_items')
.select('*')
.eq('order_id', orderId as string)
.execute()
if (res.error == null && res.data != null) {
fetchedItems.value = (res.data as UTSJSONObject[]).map((p: UTSJSONObject) : UTSJSONObject => {
return {
image: p['image_url'] != null ? (p['image_url'] as string) : '/static/logo.png',
name: p['product_name'] != null ? (p['product_name'] as string) : '--',
sku_info: p['sku_name'] != null ? (p['sku_name'] as string) : (p['specifications'] != null ? (p['specifications'] as string) : '-'),
price: p['price'] != null ? p['price'] : 0,
quantity: p['quantity'] != null ? p['quantity'] : 0
} as UTSJSONObject
})
}
} catch (e) {
console.error('fetchItems error:', e)
} finally {
itemsLoading.value = false
}
}
const close = () => {
emit('update:visible', false)
isClosing.value = true
setTimeout(() => {
isClosing.value = false
emit('update:visible', false)
}, 280)
}
watch(() => props.visible, (newVal) => {
if (newVal) {
isClosing.value = false
activeTab.value = 0
fetchedItems.value = []
fetchItems()
}
})
</script>
@@ -244,7 +323,12 @@ watch(() => props.visible, (newVal) => {
background-color: rgba(0,0,0,0.5);
z-index: 2000;
display: flex;
flex-direction: row;
justify-content: flex-end;
animation: drawerMaskFadeIn 0.3s ease-out;
&.closing {
animation: drawerMaskFadeOut 0.28s ease-in forwards;
}
}
.drawer-container {
@@ -255,6 +339,27 @@ watch(() => props.visible, (newVal) => {
box-shadow: -2px 0 8px rgba(0,0,0,0.1);
display: flex;
flex-direction: column;
animation: drawerSlideIn 0.3s cubic-bezier(0.23, 1, 0.32, 1);
&.closing {
animation: drawerSlideOut 0.28s cubic-bezier(0.755, 0.05, 0.855, 0.06) forwards;
}
}
@keyframes drawerSlideIn {
from { transform: translateX(100%); }
to { transform: translateX(0); }
}
@keyframes drawerSlideOut {
from { transform: translateX(0); }
to { transform: translateX(100%); }
}
@keyframes drawerMaskFadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes drawerMaskFadeOut {
from { opacity: 1; }
to { opacity: 0; }
}
.drawer-header {
@@ -405,6 +510,8 @@ watch(() => props.visible, (newVal) => {
/* 记录 */
.timeline { padding: 24px; background-color: #fff; }
.empty-logs { padding: 40px 24px; background-color: #fff; display: flex; align-items: center; justify-content: center; }
.empty-logs-text { font-size: 13px; color: #bfbfbf; }
.timeline-item { position: relative; padding-left: 24px; padding-bottom: 24px; }
.dot { position: absolute; left: 0; top: 4px; width: 10px; height: 10px; border-radius: 5px; background-color: #1890ff; z-index: 2; }
.line { position: absolute; left: 4.5px; top: 14px; bottom: -4px; width: 1px; background-color: #e8e8e8; }