完成订单管理的数据库链接和展示
This commit is contained in:
@@ -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