完成订单管理的数据库链接和展示
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<view v-if="visible" class="drawer-mask" @click="close">
|
<view v-if="visible" class="drawer-mask" :class="{ closing: isClosing }" @click="close">
|
||||||
<view class="drawer-container" @click.stop>
|
<view class="drawer-container" :class="{ closing: isClosing }" @click.stop>
|
||||||
<view class="drawer-header">
|
<view class="drawer-header">
|
||||||
<view class="header-left">
|
<view class="header-left">
|
||||||
<text class="title">订单详情</text>
|
<text class="title">订单详情</text>
|
||||||
@@ -36,12 +36,12 @@
|
|||||||
<text class="value status-val">¥ {{ orderInfo['paidAmount'] }}</text>
|
<text class="value status-val">¥ {{ orderInfo['paidAmount'] }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="summary-grid">
|
<view class="summary-grid">
|
||||||
<text class="label">支付方式</text>
|
<text class="label">支付状态</text>
|
||||||
<text class="value">{{ orderInfo['payMethod'] }}</text>
|
<text class="value">{{ orderInfo['payMethod'] }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="summary-grid">
|
<view class="summary-grid">
|
||||||
<text class="label">配送人员</text>
|
<text class="label">支付时间</text>
|
||||||
<text class="value">{{ orderInfo['delivery_name'] }}</text>
|
<text class="value">{{ orderInfo['payTime'] || '--' }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -108,7 +108,7 @@
|
|||||||
</view>
|
</view>
|
||||||
<view class="info-item">
|
<view class="info-item">
|
||||||
<text class="label">商品总数:</text>
|
<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>
|
||||||
<view class="info-item">
|
<view class="info-item">
|
||||||
<text class="label">产品金额:</text>
|
<text class="label">产品金额:</text>
|
||||||
@@ -179,7 +179,7 @@
|
|||||||
</view>
|
</view>
|
||||||
<view class="p-td p-price">¥{{ p['price'] }}</view>
|
<view class="p-td p-price">¥{{ p['price'] }}</view>
|
||||||
<view class="p-td p-num">{{ p['quantity'] }}</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>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -187,13 +187,16 @@
|
|||||||
|
|
||||||
<!-- 订单记录 -->
|
<!-- 订单记录 -->
|
||||||
<view v-if="activeTab === 2" class="info-section">
|
<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 v-for="(log, li) in logs" :key="li" class="timeline-item">
|
||||||
<view class="dot"></view>
|
<view class="dot"></view>
|
||||||
<view class="line" v-if="li !== logs.length - 1"></view>
|
<view class="line" v-if="li !== logs.length - 1"></view>
|
||||||
<view class="log-content">
|
<view class="log-content">
|
||||||
<text class="log-title">{{ log.title }}</text>
|
<text class="log-title">{{ log['title'] }}</text>
|
||||||
<text class="log-time">{{ log.time }}</text>
|
<text class="log-time">{{ log['time'] }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -206,6 +209,7 @@
|
|||||||
|
|
||||||
<script setup lang="uts">
|
<script setup lang="uts">
|
||||||
import { ref, computed, watch } from 'vue'
|
import { ref, computed, watch } from 'vue'
|
||||||
|
import { supabase } from '@/components/supadb/aksupainstance.uts'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
visible: { type: Boolean, default: false },
|
visible: { type: Boolean, default: false },
|
||||||
@@ -217,22 +221,97 @@ const emit = defineEmits(['update:visible'])
|
|||||||
const activeTab = ref(0)
|
const activeTab = ref(0)
|
||||||
const tabs = ['订单信息', '商品信息', '订单记录']
|
const tabs = ['订单信息', '商品信息', '订单记录']
|
||||||
|
|
||||||
const productItems = computed<UTSJSONObject[]>(() => {
|
const fetchedItems = ref<UTSJSONObject[]>([])
|
||||||
return (props.orderInfo['items'] || []) as 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([
|
const fmtTime = (timeStr : string | null) : string => {
|
||||||
{ title: '订单生成', time: '2026-02-27 15:47:25' },
|
if (timeStr == null || timeStr == '') return ''
|
||||||
{ title: '支付成功', time: '2026-02-27 15:48:30' }
|
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 = () => {
|
const close = () => {
|
||||||
|
isClosing.value = true
|
||||||
|
setTimeout(() => {
|
||||||
|
isClosing.value = false
|
||||||
emit('update:visible', false)
|
emit('update:visible', false)
|
||||||
|
}, 280)
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(() => props.visible, (newVal) => {
|
watch(() => props.visible, (newVal) => {
|
||||||
if (newVal) {
|
if (newVal) {
|
||||||
|
isClosing.value = false
|
||||||
activeTab.value = 0
|
activeTab.value = 0
|
||||||
|
fetchedItems.value = []
|
||||||
|
fetchItems()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
@@ -244,7 +323,12 @@ watch(() => props.visible, (newVal) => {
|
|||||||
background-color: rgba(0,0,0,0.5);
|
background-color: rgba(0,0,0,0.5);
|
||||||
z-index: 2000;
|
z-index: 2000;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
|
animation: drawerMaskFadeIn 0.3s ease-out;
|
||||||
|
&.closing {
|
||||||
|
animation: drawerMaskFadeOut 0.28s ease-in forwards;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.drawer-container {
|
.drawer-container {
|
||||||
@@ -255,6 +339,27 @@ watch(() => props.visible, (newVal) => {
|
|||||||
box-shadow: -2px 0 8px rgba(0,0,0,0.1);
|
box-shadow: -2px 0 8px rgba(0,0,0,0.1);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
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 {
|
.drawer-header {
|
||||||
@@ -405,6 +510,8 @@ watch(() => props.visible, (newVal) => {
|
|||||||
|
|
||||||
/* 记录 */
|
/* 记录 */
|
||||||
.timeline { padding: 24px; background-color: #fff; }
|
.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; }
|
.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; }
|
.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; }
|
.line { position: absolute; left: 4.5px; top: 14px; bottom: -4px; width: 1px; background-color: #e8e8e8; }
|
||||||
|
|||||||
@@ -77,7 +77,7 @@
|
|||||||
<view class="th col-product">商品信息</view>
|
<view class="th col-product">商品信息</view>
|
||||||
<view class="th col-user">用户信息</view>
|
<view class="th col-user">用户信息</view>
|
||||||
<view class="th col-price">实际支付</view>
|
<view class="th col-price">实际支付</view>
|
||||||
<view class="th col-pay">支付方式</view>
|
<view class="th col-pay">支付状态</view>
|
||||||
<view class="th col-time">支付时间</view>
|
<view class="th col-time">支付时间</view>
|
||||||
<view class="th col-status">订单状态</view>
|
<view class="th col-status">订单状态</view>
|
||||||
<view class="th col-op">操作</view>
|
<view class="th col-op">操作</view>
|
||||||
@@ -89,6 +89,12 @@
|
|||||||
<text>加载中...</text>
|
<text>加载中...</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<!-- 错误状态 -->
|
||||||
|
<view v-else-if="fetchError != ''" class="error-state">
|
||||||
|
<text class="error-text">{{ fetchError }}</text>
|
||||||
|
<button class="retry-btn" @click="fetchData">重新加载</button>
|
||||||
|
</view>
|
||||||
|
|
||||||
<!-- 无数据状态 -->
|
<!-- 无数据状态 -->
|
||||||
<view v-else-if="filteredOrders.length === 0" class="empty-state">
|
<view v-else-if="filteredOrders.length === 0" class="empty-state">
|
||||||
<text>暂无订单数据</text>
|
<text>暂无订单数据</text>
|
||||||
@@ -105,6 +111,7 @@
|
|||||||
<view class="td col-order">
|
<view class="td col-order">
|
||||||
<text class="order-sn">{{ item['sn'] }}</text>
|
<text class="order-sn">{{ item['sn'] }}</text>
|
||||||
<text class="order-type" :class="item['typeColor']">[{{ item['typeName'] }}]</text>
|
<text class="order-type" :class="item['typeColor']">[{{ item['typeName'] }}]</text>
|
||||||
|
<text class="order-time">{{ item['created_at'] }}</text>
|
||||||
<text v-if="item['cancelStatus'] != ''" class="cancel-text">{{ item['cancelStatus'] }}</text>
|
<text v-if="item['cancelStatus'] != ''" class="cancel-text">{{ item['cancelStatus'] }}</text>
|
||||||
</view>
|
</view>
|
||||||
<!-- 商品信息 -->
|
<!-- 商品信息 -->
|
||||||
@@ -138,26 +145,42 @@
|
|||||||
</view>
|
</view>
|
||||||
<!-- 订单状态 -->
|
<!-- 订单状态 -->
|
||||||
<view class="td col-status">
|
<view class="td col-status">
|
||||||
<text class="status-text">{{ item['statusName'] }}</text>
|
<text class="status-text" :style="{ color: item['statusColor'] }">{{ item['statusName'] }}</text>
|
||||||
</view>
|
</view>
|
||||||
<!-- 操作 -->
|
<!-- 操作 -->
|
||||||
<view class="td col-op overflow-visible no-wrap">
|
<view class="td col-op overflow-visible no-wrap">
|
||||||
<view class="op-links">
|
<view class="op-links">
|
||||||
<text class="op-link primary" @click.stop="handleAction('edit', item['sn'] as string)">编辑</text>
|
<text class="op-link primary" @click.stop="viewDetail(item)">详情</text>
|
||||||
|
<template v-if="(item['orderStatus'] as number) === 2">
|
||||||
<view class="divider-v"></view>
|
<view class="divider-v"></view>
|
||||||
<view class="op-dropdown-container">
|
<text class="op-link primary" @click.stop="shipOrder(item)">发货</text>
|
||||||
<view class="op-link-more" @click.stop="toggleDropdown(item['sn'] as string)">
|
</template>
|
||||||
|
<view class="divider-v"></view>
|
||||||
|
<view class="op-dropdown-container" @mouseenter="activeDropdownId = (item['sn'] as string)" @mouseleave="activeDropdownId = ''">
|
||||||
|
<view class="op-link-more">
|
||||||
<text class="more-text">更多</text>
|
<text class="more-text">更多</text>
|
||||||
<view :class="{ 'arrow-up-blue': activeDropdownId === item['sn'], 'arrow-down-blue': activeDropdownId !== item['sn'] }"></view>
|
<view :class="{ 'arrow-up-blue': activeDropdownId === item['sn'], 'arrow-down-blue': activeDropdownId !== item['sn'] }"></view>
|
||||||
</view>
|
</view>
|
||||||
<!-- 浮动菜单 -->
|
<!-- 浮动菜单 -->
|
||||||
<view v-if="activeDropdownId === (item['sn'] as string)" class="dropdown-menu">
|
<view v-if="activeDropdownId === (item['sn'] as string)" class="dropdown-menu">
|
||||||
<view class="dropdown-item" @click.stop="viewDetail(item)">
|
<view class="dropdown-item" @click.stop="handleMore('print_receipt', item)">
|
||||||
<text class="item-text">订单详情</text>
|
<text class="item-text">小票打印</text>
|
||||||
|
</view>
|
||||||
|
<view class="dropdown-item" @click.stop="handleMore('edit_address', item)">
|
||||||
|
<text class="item-text">修改地址</text>
|
||||||
|
</view>
|
||||||
|
<view class="dropdown-item" @click.stop="handleMore('remark', item)">
|
||||||
|
<text class="item-text">订单备注</text>
|
||||||
|
</view>
|
||||||
|
<view class="dropdown-item" @click.stop="handleMore('refund', item)">
|
||||||
|
<text class="item-text">立即退款</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="dropdown-item item-danger" @click.stop="deleteOrder(item)">
|
<view class="dropdown-item item-danger" @click.stop="deleteOrder(item)">
|
||||||
<text class="item-text text-red">删除订单</text>
|
<text class="item-text text-red">删除订单</text>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="dropdown-item" @click.stop="handleMore('packing_slip', item)">
|
||||||
|
<text class="item-text">配货单打印</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -169,7 +192,7 @@
|
|||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<view class="pagination-footer">
|
<view class="pagination-footer">
|
||||||
<view class="page-left">
|
<view class="page-left">
|
||||||
<text class="count-text">共 {{ orderData.length }} 条</text>
|
<text class="count-text">共 {{ filteredOrders.length }} 条</text>
|
||||||
<view class="page-size-select">
|
<view class="page-size-select">
|
||||||
<text>10条/页</text>
|
<text>10条/页</text>
|
||||||
<view class="arrow-down"></view>
|
<view class="arrow-down"></view>
|
||||||
@@ -209,6 +232,7 @@ const searchKeyword = ref('')
|
|||||||
const activeDropdownId = ref('')
|
const activeDropdownId = ref('')
|
||||||
const showDetail = ref(false)
|
const showDetail = ref(false)
|
||||||
const selectedOrder = ref<UTSJSONObject>({} as UTSJSONObject)
|
const selectedOrder = ref<UTSJSONObject>({} as UTSJSONObject)
|
||||||
|
const fetchError = ref('')
|
||||||
|
|
||||||
// 时间显示格式化
|
// 时间显示格式化
|
||||||
const formatTime = (timeStr : string | null) : string => {
|
const formatTime = (timeStr : string | null) : string => {
|
||||||
@@ -236,6 +260,18 @@ const statusTabs = [
|
|||||||
|
|
||||||
const orderData = ref<UTSJSONObject[]>([])
|
const orderData = ref<UTSJSONObject[]>([])
|
||||||
|
|
||||||
|
// 订单状态颜色映射
|
||||||
|
const getStatusColor = (status : number) : string => {
|
||||||
|
if (status === 1) return '#faad14'
|
||||||
|
if (status === 2) return '#1890ff'
|
||||||
|
if (status === 3) return '#1890ff'
|
||||||
|
if (status === 4) return '#52c41a'
|
||||||
|
if (status === 5) return '#999999'
|
||||||
|
if (status === 6) return '#fa8c16'
|
||||||
|
if (status === 7) return '#f5222d'
|
||||||
|
return '#333333'
|
||||||
|
}
|
||||||
|
|
||||||
const filteredOrders = computed<UTSJSONObject[]>(() => {
|
const filteredOrders = computed<UTSJSONObject[]>(() => {
|
||||||
let list = orderData.value
|
let list = orderData.value
|
||||||
|
|
||||||
@@ -249,9 +285,9 @@ const filteredOrders = computed<UTSJSONObject[]>(() => {
|
|||||||
if (searchKeyword.value.trim() !== '') {
|
if (searchKeyword.value.trim() !== '') {
|
||||||
const kw = searchKeyword.value.toLowerCase()
|
const kw = searchKeyword.value.toLowerCase()
|
||||||
list = list.filter((o : UTSJSONObject) : boolean => {
|
list = list.filter((o : UTSJSONObject) : boolean => {
|
||||||
const sn = (o['sn'] as string).toLowerCase()
|
const sn = o['sn'] != null ? (o['sn'] as string).toLowerCase() : ''
|
||||||
const user = o['user'] as UTSJSONObject
|
const user = o['user'] as UTSJSONObject
|
||||||
const phone = user['phone'] as string
|
const phone = (user != null && user['phone'] != null) ? (user['phone'] as string) : ''
|
||||||
return sn.includes(kw) || phone.includes(kw)
|
return sn.includes(kw) || phone.includes(kw)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -261,62 +297,107 @@ const filteredOrders = computed<UTSJSONObject[]>(() => {
|
|||||||
|
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
|
fetchError.value = ''
|
||||||
|
|
||||||
|
// 获取当前商家 ID(merchant_id = ak_users.id = Supabase Auth UUID)
|
||||||
|
const currentMerchantId = supabase.getSession().user?.getString('id')
|
||||||
|
if (currentMerchantId == null || currentMerchantId == '') {
|
||||||
|
loading.value = false
|
||||||
|
fetchError.value = '未获取到商家身份信息,请重新登录后再试'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 从 ml_orders_detail_view 视图读取数据
|
|
||||||
const res = await supabase
|
const res = await supabase
|
||||||
.from('ml_orders_detail_view')
|
.from('ml_orders_detail_view')
|
||||||
.select('*')
|
.select('*')
|
||||||
|
.eq('merchant_id', currentMerchantId)
|
||||||
.order('created_at', { ascending: false })
|
.order('created_at', { ascending: false })
|
||||||
.execute()
|
.execute()
|
||||||
|
|
||||||
if (res.error == null && res.data != null) {
|
if (res.error == null && res.data != null) {
|
||||||
const rawData = res.data as UTSJSONObject[]
|
const rawData = res.data as UTSJSONObject[]
|
||||||
orderData.value = rawData.map((item: UTSJSONObject) : UTSJSONObject => {
|
orderData.value = rawData.map((item: UTSJSONObject) : UTSJSONObject => {
|
||||||
|
// 解析 shipping_address JSONB(含 receiver_name/receiver_phone/province/city/district/address_detail)
|
||||||
|
const rawAddr = item['shipping_address']
|
||||||
|
let receiverName = item['customer_name'] != null ? (item['customer_name'] as string) : ''
|
||||||
|
let receiverPhone = item['customer_phone'] != null ? (item['customer_phone'] as string) : ''
|
||||||
|
let fullAddress = '暂无地址信息'
|
||||||
|
if (rawAddr != null) {
|
||||||
|
const addr = rawAddr as UTSJSONObject
|
||||||
|
const rn = addr['receiver_name']
|
||||||
|
const rp = addr['receiver_phone']
|
||||||
|
if (rn != null && rn !== '') receiverName = rn as string
|
||||||
|
if (rp != null && rp !== '') receiverPhone = rp as string
|
||||||
|
const prov = addr['province'] != null ? (addr['province'] as string) : ''
|
||||||
|
const cit = addr['city'] != null ? (addr['city'] as string) : ''
|
||||||
|
const dist = addr['district'] != null ? (addr['district'] as string) : ''
|
||||||
|
const det = addr['address_detail'] != null ? (addr['address_detail'] as string) : ''
|
||||||
|
const combined = prov + cit + dist + det
|
||||||
|
if (combined !== '') fullAddress = combined
|
||||||
|
}
|
||||||
|
|
||||||
|
const orderStatusNum = item['order_status'] as number
|
||||||
|
|
||||||
|
// cancelStatus:取消原因展示
|
||||||
|
let cancelStatus = ''
|
||||||
|
if (orderStatusNum === 5) {
|
||||||
|
const reason = item['cancel_reason']
|
||||||
|
cancelStatus = (reason != null && reason !== '') ? ('已取消:' + (reason as string)) : '用户已取消'
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
sn: item['order_no'],
|
id: item['id'] != null ? (item['id'] as string) : '',
|
||||||
|
sn: item['order_no'] != null ? item['order_no'] : '--',
|
||||||
typeName: '普通订单',
|
typeName: '普通订单',
|
||||||
typeColor: 'green',
|
typeColor: 'green',
|
||||||
orderStatus: item['order_status'],
|
orderStatus: orderStatusNum,
|
||||||
cancelStatus: item['order_status'] === 5 ? '用户已取消' : '',
|
cancelStatus: cancelStatus,
|
||||||
product: {
|
product: {
|
||||||
img: '/static/logo.png', // 默认占位图
|
img: '/static/logo.png',
|
||||||
name: '订单概要 (详情查看)'
|
name: '订单详情查看'
|
||||||
} as UTSJSONObject,
|
} as UTSJSONObject,
|
||||||
items: item['order_items'] as UTSJSONObject[], // 尝试获取子项
|
items: null, // ml_orders_detail_view 视图未聚合 order_items,详情抽屉另行处理
|
||||||
user: {
|
user: {
|
||||||
name: (item['customer_name'] || '未知用户') as string,
|
name: (item['customer_name'] || '未知用户') as string,
|
||||||
id: (item['user_id'] || '--') as string,
|
id: (item['user_id'] || '--') as string,
|
||||||
phone: (item['customer_phone'] || '--') as string
|
phone: receiverPhone != '' ? receiverPhone : (item['customer_phone'] != null ? (item['customer_phone'] as string) : '--')
|
||||||
} as UTSJSONObject,
|
} as UTSJSONObject,
|
||||||
isPaid: item['payment_status'] === 2,
|
isPaid: item['payment_status'] === 2,
|
||||||
actualPrice: item['total_amount'], // 总金额
|
actualPrice: item['paid_amount'] != null ? item['paid_amount'] : 0, // 已付金额(修正:原为 total_amount)
|
||||||
paidAmount: item['paid_amount'] != null ? item['paid_amount'] : 0.00, // 支付金额
|
paidAmount: item['paid_amount'] != null ? item['paid_amount'] : 0,
|
||||||
payMethod: item['payment_status_name'] || '--',
|
payMethod: item['payment_status_name'] != null ? (item['payment_status_name'] as string) : '--', // 支付状态(ml_orders 表无支付方式字段)
|
||||||
payTime: formatTime(item['paid_at'] as string | null),
|
payTime: formatTime(item['paid_at'] as string | null),
|
||||||
created_at: formatTime(item['created_at'] as string | null),
|
created_at: formatTime(item['created_at'] as string | null),
|
||||||
statusName: item['order_status_name'] || '未知',
|
statusName: item['order_status_name'] != null ? (item['order_status_name'] as string) : '未知',
|
||||||
primaryAction: item['order_status'] === 1 ? '立即支付' : '',
|
statusColor: getStatusColor(orderStatusNum),
|
||||||
total_num: Array.isArray(item['order_items']) ? (item['order_items'] as Array<any>).length : 0,
|
primaryAction: orderStatusNum === 1 ? '立即支付' : '',
|
||||||
total_price: item['product_amount'] != null ? item['product_amount'] : 0.00, // 产品价格
|
total_num: 0,
|
||||||
coupon_price: item['discount_amount'] != null ? item['discount_amount'] : 0.00, // 折扣价
|
total_price: item['product_amount'] != null ? item['product_amount'] : 0,
|
||||||
shipping_fee: item['shipping_fee'] != null ? item['shipping_fee'] : 0.00, // 运费
|
coupon_price: item['discount_amount'] != null ? item['discount_amount'] : 0,
|
||||||
|
shipping_fee: item['shipping_fee'] != null ? item['shipping_fee'] : 0,
|
||||||
deduction_price: 0,
|
deduction_price: 0,
|
||||||
user_address: item['shipping_address'] != null ? (typeof item['shipping_address'] === 'string' ? item['shipping_address'] : JSON.stringify(item['shipping_address'])) : '暂无地址信息',
|
user_address: fullAddress,
|
||||||
real_name: item['customer_name'] != null ? item['customer_name'] : '', // 消费者名字
|
real_name: receiverName,
|
||||||
user_phone: item['customer_phone'] != null ? item['customer_phone'] : '',
|
user_phone: receiverPhone,
|
||||||
delivery_name: item['merchant_name'] != null ? item['merchant_name'] : '--', // 配送人员
|
delivery_name: item['merchant_name'] != null ? (item['merchant_name'] as string) : '--',
|
||||||
store_name: item['shop_name'] != null ? item['shop_name'] : '--', // 店铺名字
|
store_name: item['shop_name'] != null ? (item['shop_name'] as string) : '--',
|
||||||
mark: item['remark'] != null ? item['remark'] : '-',
|
mark: item['remark'] != null ? (item['remark'] as string) : '-',
|
||||||
remark: item['merchant_memo'] != null ? item['merchant_memo'] : '-'
|
remark: item['merchant_memo'] != null ? (item['merchant_memo'] as string) : '-',
|
||||||
|
shipped_at: item['shipped_at'] != null ? (item['shipped_at'] as string) : '',
|
||||||
|
delivered_at: item['delivered_at'] != null ? (item['delivered_at'] as string) : '',
|
||||||
|
completed_at: item['completed_at'] != null ? (item['completed_at'] as string) : ''
|
||||||
} as UTSJSONObject
|
} as UTSJSONObject
|
||||||
})
|
})
|
||||||
|
|
||||||
// 更新统计数据
|
// 更新统计数据
|
||||||
updateTabCounts()
|
updateTabCounts()
|
||||||
} else {
|
} else {
|
||||||
|
fetchError.value = '加载订单失败,请检查网络后重试'
|
||||||
console.error('Fetch orders error:', res.error)
|
console.error('Fetch orders error:', res.error)
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
fetchError.value = '加载订单时发生异常,请检查网络后重试'
|
||||||
console.error('Fetch orders exception:', e)
|
console.error('Fetch orders exception:', e)
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
@@ -364,6 +445,39 @@ const viewDetail = (order: UTSJSONObject) => {
|
|||||||
closeDropdowns()
|
closeDropdowns()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 发货操作
|
||||||
|
const shipOrder = async (order: UTSJSONObject) => {
|
||||||
|
uni.showModal({
|
||||||
|
title: '确认发货',
|
||||||
|
content: `确定将订单 ${order['sn']} 标记为已发货?`,
|
||||||
|
success: async (res) => {
|
||||||
|
if (res.confirm) {
|
||||||
|
try {
|
||||||
|
const result = await supabase
|
||||||
|
.from('ml_orders')
|
||||||
|
.update({ order_status: 3, shipping_status: 2, shipped_at: new Date().toISOString() } as UTSJSONObject)
|
||||||
|
.eq('order_no', order['sn'] as string)
|
||||||
|
.execute()
|
||||||
|
if (result.error == null) {
|
||||||
|
uni.showToast({ title: '发货成功' })
|
||||||
|
fetchData()
|
||||||
|
} else {
|
||||||
|
uni.showToast({ title: '操作失败', icon: 'none' })
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('shipOrder error:', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更多菜单操作(功能待接入)
|
||||||
|
const handleMore = (_action: string, _order: UTSJSONObject) => {
|
||||||
|
closeDropdowns()
|
||||||
|
uni.showToast({ title: '功能开发中', icon: 'none' })
|
||||||
|
}
|
||||||
|
|
||||||
// 删除订单 (权限占位代码)
|
// 删除订单 (权限占位代码)
|
||||||
const deleteOrder = async (order: UTSJSONObject) => {
|
const deleteOrder = async (order: UTSJSONObject) => {
|
||||||
closeDropdowns()
|
closeDropdowns()
|
||||||
@@ -400,11 +514,6 @@ const deleteOrder = async (order: UTSJSONObject) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleAction = (action: string, sn: string) => {
|
|
||||||
uni.showToast({ title: `执行操作: ${action} - ${sn}`, icon: 'none' })
|
|
||||||
closeDropdowns()
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
fetchData()
|
fetchData()
|
||||||
// 点击空白处关闭下拉
|
// 点击空白处关闭下拉
|
||||||
@@ -705,7 +814,7 @@ onMounted(() => {
|
|||||||
.col-pay { width: 100px; }
|
.col-pay { width: 100px; }
|
||||||
.col-time { width: 160px; }
|
.col-time { width: 160px; }
|
||||||
.col-status { width: 100px; }
|
.col-status { width: 100px; }
|
||||||
.col-op { width: 140px; }
|
.col-op { width: 160px; }
|
||||||
|
|
||||||
.order-sn { font-size: 13px; color: #262626; margin-bottom: 4px; text-align: left; }
|
.order-sn { font-size: 13px; color: #262626; margin-bottom: 4px; text-align: left; }
|
||||||
.order-type { font-size: 12px; text-align: left; }
|
.order-type { font-size: 12px; text-align: left; }
|
||||||
@@ -879,5 +988,40 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.page-btns-more { border: none; }
|
.page-btns-more { border: none; }
|
||||||
|
|
||||||
|
.order-time {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999;
|
||||||
|
margin-top: 2px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-unpaid {
|
||||||
|
color: #f5222d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-state {
|
||||||
|
padding: 40px 20px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-text {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #f5222d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.retry-btn {
|
||||||
|
height: 32px;
|
||||||
|
padding: 0 16px;
|
||||||
|
font-size: 14px;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: #fff;
|
||||||
|
color: #1890ff;
|
||||||
|
border: 1px solid #1890ff;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user