完成订单管理的数据库链接和展示
This commit is contained in:
@@ -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; }
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
<view class="th col-product">商品信息</view>
|
||||
<view class="th col-user">用户信息</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-status">订单状态</view>
|
||||
<view class="th col-op">操作</view>
|
||||
@@ -88,7 +88,13 @@
|
||||
<view v-if="loading" class="loading-state">
|
||||
<text>加载中...</text>
|
||||
</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">
|
||||
<text>暂无订单数据</text>
|
||||
@@ -105,6 +111,7 @@
|
||||
<view class="td col-order">
|
||||
<text class="order-sn">{{ item['sn'] }}</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>
|
||||
</view>
|
||||
<!-- 商品信息 -->
|
||||
@@ -138,26 +145,42 @@
|
||||
</view>
|
||||
<!-- 订单状态 -->
|
||||
<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 class="td col-op overflow-visible no-wrap">
|
||||
<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>
|
||||
<text class="op-link primary" @click.stop="shipOrder(item)">发货</text>
|
||||
</template>
|
||||
<view class="divider-v"></view>
|
||||
<view class="op-dropdown-container">
|
||||
<view class="op-link-more" @click.stop="toggleDropdown(item['sn'] as string)">
|
||||
<view class="op-dropdown-container" @mouseenter="activeDropdownId = (item['sn'] as string)" @mouseleave="activeDropdownId = ''">
|
||||
<view class="op-link-more">
|
||||
<text class="more-text">更多</text>
|
||||
<view :class="{ 'arrow-up-blue': activeDropdownId === item['sn'], 'arrow-down-blue': activeDropdownId !== item['sn'] }"></view>
|
||||
</view>
|
||||
<!-- 浮动菜单 -->
|
||||
<view v-if="activeDropdownId === (item['sn'] as string)" class="dropdown-menu">
|
||||
<view class="dropdown-item" @click.stop="viewDetail(item)">
|
||||
<text class="item-text">订单详情</text>
|
||||
<view class="dropdown-item" @click.stop="handleMore('print_receipt', item)">
|
||||
<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 class="dropdown-item item-danger" @click.stop="deleteOrder(item)">
|
||||
<text class="item-text text-red">删除订单</text>
|
||||
</view>
|
||||
<view class="dropdown-item" @click.stop="handleMore('packing_slip', item)">
|
||||
<text class="item-text">配货单打印</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -169,7 +192,7 @@
|
||||
<!-- 分页 -->
|
||||
<view class="pagination-footer">
|
||||
<view class="page-left">
|
||||
<text class="count-text">共 {{ orderData.length }} 条</text>
|
||||
<text class="count-text">共 {{ filteredOrders.length }} 条</text>
|
||||
<view class="page-size-select">
|
||||
<text>10条/页</text>
|
||||
<view class="arrow-down"></view>
|
||||
@@ -209,6 +232,7 @@ const searchKeyword = ref('')
|
||||
const activeDropdownId = ref('')
|
||||
const showDetail = ref(false)
|
||||
const selectedOrder = ref<UTSJSONObject>({} as UTSJSONObject)
|
||||
const fetchError = ref('')
|
||||
|
||||
// 时间显示格式化
|
||||
const formatTime = (timeStr : string | null) : string => {
|
||||
@@ -236,6 +260,18 @@ const statusTabs = [
|
||||
|
||||
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[]>(() => {
|
||||
let list = orderData.value
|
||||
|
||||
@@ -249,9 +285,9 @@ const filteredOrders = computed<UTSJSONObject[]>(() => {
|
||||
if (searchKeyword.value.trim() !== '') {
|
||||
const kw = searchKeyword.value.toLowerCase()
|
||||
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 phone = user['phone'] as string
|
||||
const phone = (user != null && user['phone'] != null) ? (user['phone'] as string) : ''
|
||||
return sn.includes(kw) || phone.includes(kw)
|
||||
})
|
||||
}
|
||||
@@ -261,62 +297,107 @@ const filteredOrders = computed<UTSJSONObject[]>(() => {
|
||||
|
||||
const fetchData = async () => {
|
||||
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 {
|
||||
// 从 ml_orders_detail_view 视图读取数据
|
||||
const res = await supabase
|
||||
.from('ml_orders_detail_view')
|
||||
.select('*')
|
||||
.eq('merchant_id', currentMerchantId)
|
||||
.order('created_at', { ascending: false })
|
||||
.execute()
|
||||
|
||||
|
||||
if (res.error == null && res.data != null) {
|
||||
const rawData = res.data as 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 {
|
||||
sn: item['order_no'],
|
||||
typeName: '普通订单',
|
||||
id: item['id'] != null ? (item['id'] as string) : '',
|
||||
sn: item['order_no'] != null ? item['order_no'] : '--',
|
||||
typeName: '普通订单',
|
||||
typeColor: 'green',
|
||||
orderStatus: item['order_status'],
|
||||
cancelStatus: item['order_status'] === 5 ? '用户已取消' : '',
|
||||
orderStatus: orderStatusNum,
|
||||
cancelStatus: cancelStatus,
|
||||
product: {
|
||||
img: '/static/logo.png', // 默认占位图
|
||||
name: '订单概要 (详情查看)'
|
||||
img: '/static/logo.png',
|
||||
name: '订单详情查看'
|
||||
} as UTSJSONObject,
|
||||
items: item['order_items'] as UTSJSONObject[], // 尝试获取子项
|
||||
user: {
|
||||
name: (item['customer_name'] || '未知用户') as string,
|
||||
items: null, // ml_orders_detail_view 视图未聚合 order_items,详情抽屉另行处理
|
||||
user: {
|
||||
name: (item['customer_name'] || '未知用户') 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,
|
||||
isPaid: item['payment_status'] === 2,
|
||||
actualPrice: item['total_amount'], // 总金额
|
||||
paidAmount: item['paid_amount'] != null ? item['paid_amount'] : 0.00, // 支付金额
|
||||
payMethod: item['payment_status_name'] || '--',
|
||||
isPaid: item['payment_status'] === 2,
|
||||
actualPrice: item['paid_amount'] != null ? item['paid_amount'] : 0, // 已付金额(修正:原为 total_amount)
|
||||
paidAmount: item['paid_amount'] != null ? item['paid_amount'] : 0,
|
||||
payMethod: item['payment_status_name'] != null ? (item['payment_status_name'] as string) : '--', // 支付状态(ml_orders 表无支付方式字段)
|
||||
payTime: formatTime(item['paid_at'] as string | null),
|
||||
created_at: formatTime(item['created_at'] as string | null),
|
||||
statusName: item['order_status_name'] || '未知',
|
||||
primaryAction: item['order_status'] === 1 ? '立即支付' : '',
|
||||
total_num: Array.isArray(item['order_items']) ? (item['order_items'] as Array<any>).length : 0,
|
||||
total_price: item['product_amount'] != null ? item['product_amount'] : 0.00, // 产品价格
|
||||
coupon_price: item['discount_amount'] != null ? item['discount_amount'] : 0.00, // 折扣价
|
||||
shipping_fee: item['shipping_fee'] != null ? item['shipping_fee'] : 0.00, // 运费
|
||||
deduction_price: 0,
|
||||
user_address: item['shipping_address'] != null ? (typeof item['shipping_address'] === 'string' ? item['shipping_address'] : JSON.stringify(item['shipping_address'])) : '暂无地址信息',
|
||||
real_name: item['customer_name'] != null ? item['customer_name'] : '', // 消费者名字
|
||||
user_phone: item['customer_phone'] != null ? item['customer_phone'] : '',
|
||||
delivery_name: item['merchant_name'] != null ? item['merchant_name'] : '--', // 配送人员
|
||||
store_name: item['shop_name'] != null ? item['shop_name'] : '--', // 店铺名字
|
||||
mark: item['remark'] != null ? item['remark'] : '-',
|
||||
remark: item['merchant_memo'] != null ? item['merchant_memo'] : '-'
|
||||
statusName: item['order_status_name'] != null ? (item['order_status_name'] as string) : '未知',
|
||||
statusColor: getStatusColor(orderStatusNum),
|
||||
primaryAction: orderStatusNum === 1 ? '立即支付' : '',
|
||||
total_num: 0,
|
||||
total_price: item['product_amount'] != null ? item['product_amount'] : 0,
|
||||
coupon_price: item['discount_amount'] != null ? item['discount_amount'] : 0,
|
||||
shipping_fee: item['shipping_fee'] != null ? item['shipping_fee'] : 0,
|
||||
deduction_price: 0,
|
||||
user_address: fullAddress,
|
||||
real_name: receiverName,
|
||||
user_phone: receiverPhone,
|
||||
delivery_name: item['merchant_name'] != null ? (item['merchant_name'] as string) : '--',
|
||||
store_name: item['shop_name'] != null ? (item['shop_name'] as string) : '--',
|
||||
mark: item['remark'] != null ? (item['remark'] as string) : '-',
|
||||
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
|
||||
})
|
||||
|
||||
|
||||
// 更新统计数据
|
||||
updateTabCounts()
|
||||
} else {
|
||||
fetchError.value = '加载订单失败,请检查网络后重试'
|
||||
console.error('Fetch orders error:', res.error)
|
||||
}
|
||||
} catch (e) {
|
||||
fetchError.value = '加载订单时发生异常,请检查网络后重试'
|
||||
console.error('Fetch orders exception:', e)
|
||||
} finally {
|
||||
loading.value = false
|
||||
@@ -364,6 +445,39 @@ const viewDetail = (order: UTSJSONObject) => {
|
||||
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) => {
|
||||
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(() => {
|
||||
fetchData()
|
||||
// 点击空白处关闭下拉
|
||||
@@ -705,7 +814,7 @@ onMounted(() => {
|
||||
.col-pay { width: 100px; }
|
||||
.col-time { width: 160px; }
|
||||
.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-type { font-size: 12px; text-align: left; }
|
||||
@@ -879,5 +988,40 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
.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>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user