consumerm模块完成度90%,完善消费者和商家端数据库表,商品、聊天、订单数据对接好了supabase,和商家端对接了聊天功能,安卓端编译通过了css样式,剩余几个页面在处理函数规范问题

This commit is contained in:
cyh666666
2026-02-24 17:17:49 +08:00
parent e2f1dfb097
commit e606c597ca
174 changed files with 37917 additions and 4444 deletions

View File

@@ -29,6 +29,7 @@
>
<text class="tab-name">{{ tab.name }}</text>
<text v-if="tab.count > 0" class="tab-count">{{ tab.count }}</text>
<view v-if="activeTab === tab.id" class="active-indicator"></view>
</view>
</view>
</scroll-view>
@@ -179,9 +180,38 @@ onBackPress((options) => {
return false
})
// 定义标签页类型
type OrderTabItem = {
id: string,
name: string,
count: number
}
// 定义订单产品类型
type OrderProduct = {
id: string,
name: string,
price: number,
image: string,
spec: string,
quantity: number
}
// 定义订单类型
type OrderItem = {
id: string,
order_no: string,
status: number,
create_time: string,
product_amount: number,
shipping_fee: number,
total_amount: number,
products: OrderProduct[]
}
// 响应式数据
const orders = ref<any[]>([])
const allOrdersList = ref<any[]>([]) // Store all fetched orders for client-side filtering
const orders = ref<OrderItem[]>([])
const allOrdersList = ref<OrderItem[]>([]) // Store all fetched orders for client-side filtering
const loading = ref<boolean>(false)
const loadingMore = ref<boolean>(false)
const hasMore = ref<boolean>(true)
@@ -190,8 +220,8 @@ const page = ref<number>(1)
const activeTab = ref<string>('all')
const searchKeyword = ref<string>('')
// 订单标签页
const orderTabs = reactive([
// 订单标签页 - 使用 ref 以便整体替换
const orderTabs = ref<OrderTabItem[]>([
{ id: 'all', name: '全部', count: 0 },
{ id: 'pending', name: '待付款', count: 0 },
{ id: 'shipping', name: '待发货', count: 0 },
@@ -203,44 +233,82 @@ const orderTabs = reactive([
// Removed Mock Data
// 计算属性:根据当前标签筛选订单
const filteredOrders = computed(() => {
// 辅助函数:获取状态码
const getStatusByTab = (tabId: string): number => {
if (tabId == 'pending') return 1
if (tabId == 'shipping') return 2
if (tabId == 'delivering') return 3
if (tabId == 'completed') return 4
if (tabId == 'cancelled') return 5
return 0
}
// 辅助函数:解析规格文本
const parseSpecText = (specs: any): string => {
if (specs == null) return ''
if (typeof specs === 'string') return specs
// 对于对象类型尝试转为JSON字符串或简单处理
try {
return JSON.stringify(specs)
} catch (e) {
return ''
}
}
// 辅助函数:更新标签计数
const updateTabsCounts = (allOrders: any[]) => {
// 直接重新赋值整个数组
const tabsData = orderTabs.value
// 计算各状态数量
const countAll = allOrders.length
const countPending = allOrders.filter((o: any) => {
const obj = o as Record<string, any>
return obj['status'] === 1
}).length
const countShipping = allOrders.filter((o: any) => {
const obj = o as Record<string, any>
return obj['status'] === 2
}).length
const countDelivering = allOrders.filter((o: any) => {
const obj = o as Record<string, any>
return obj['status'] === 3
}).length
const countCompleted = allOrders.filter((o: any) => {
const obj = o as Record<string, any>
return obj['status'] === 4
}).length
const countCancelled = allOrders.filter((o: any) => {
const obj = o as Record<string, any>
return obj['status'] === 5
}).length
// 更新数组元素
const tabsArr = tabsData as any[]
const tab0 = tabsArr[0] as Record<string, any>
tab0['count'] = countAll
const tab1 = tabsArr[1] as Record<string, any>
tab1['count'] = countPending
const tab2 = tabsArr[2] as Record<string, any>
tab2['count'] = countShipping
const tab3 = tabsArr[3] as Record<string, any>
tab3['count'] = countDelivering
const tab4 = tabsArr[4] as Record<string, any>
tab4['count'] = countCompleted
const tab5 = tabsArr[5] as Record<string, any>
tab5['count'] = countCancelled
}
// 辅助函数:按标签筛选订单
const filterOrdersByTab = () => {
if (activeTab.value === 'all') {
return orders.value
orders.value = allOrdersList.value
} else {
const targetStatus = getStatusByTab(activeTab.value)
orders.value = allOrdersList.value.filter((o: OrderItem) => {
return o.status === targetStatus
})
}
const statusMap: Record<string, number> = {
'pending': 1,
'shipping': 2,
'delivering': 3,
'completed': 4,
'cancelled': 5
}
const targetStatus = statusMap[activeTab.value]
return orders.value.filter(order => order.status === targetStatus)
})
// 生命周期
onLoad((options) => {
if (options['status']) {
const status = options['status'] as string
if (['all', 'pending', 'shipping', 'delivering', 'completed', 'cancelled'].includes(status)) {
activeTab.value = status
}
}
if (options['type']) {
const type = options['type'] as string
if (type === 'pending') activeTab.value = 'pending'
else if (type === 'shipped') activeTab.value = 'delivering' // 映射到待收货
else if (type === 'review') activeTab.value = 'completed' // 映射到已完成
else if (type === 'refund') activeTab.value = 'all' // 申请售后默认显示全部
}
})
onShow(() => {
loadOrders()
})
}
// 加载订单数据
const loadOrders = async () => {
@@ -251,32 +319,67 @@ const loadOrders = async () => {
const fetchedOrders = await supabaseService.getOrders(0)
// Map to View Model
const mappedOrders = fetchedOrders.map((order: any) => ({
id: order.id,
order_no: order.order_no,
status: order.order_status,
create_time: order.created_at,
product_amount: order.product_amount || order.actual_amount,
shipping_fee: order.delivery_fee,
total_amount: order.actual_amount,
products: (order.ml_order_items || []).map((item: any) => ({
id: item.product_id,
name: item.product_name,
price: item.price,
image: item.image_url || '/static/default-product.png',
spec: item.specifications ? (typeof item.specifications === 'string' ? item.specifications : Object.values(item.specifications).join(' ')) : '',
quantity: item.quantity
}))
}))
const mappedOrders: any[] = []
for (let i = 0; i < fetchedOrders.length; i++) {
const order = fetchedOrders[i]
const orderObj = order as Record<string, any>
const items = orderObj['ml_order_items'] as any[]
const productsList: any[] = []
if (items != null) {
for (let j = 0; j < items.length; j++) {
const item = items[j]
const itemObj = item as Record<string, any>
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']
})
}
}
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,
products: productsList
})
}
// Sort by created_at desc
mappedOrders.sort((a: any, b: any) => {
const timeA = new Date(a.create_time).getTime()
const timeB = new Date(b.create_time).getTime()
const aObj = a as Record<string, any>
const bObj = b as Record<string, any>
const timeA = new Date(aObj['create_time'] as string).getTime()
const timeB = new Date(bObj['create_time'] as string).getTime()
return timeB - timeA
})
allOrdersList.value = mappedOrders
// 将 mappedOrders 转换为 OrderItem[] 类型
const typedOrders: OrderItem[] = []
for (let i = 0; i < mappedOrders.length; i++) {
const mo = mappedOrders[i] as Record<string, any>
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
// Update tab counts
updateTabsCounts(mappedOrders)
@@ -292,41 +395,77 @@ const loadOrders = async () => {
}
}
const updateTabsCounts = (allOrders: any[]) => {
orderTabs[0].count = allOrders.length
orderTabs[1].count = allOrders.filter((o: any) => o.status === 1).length
orderTabs[2].count = allOrders.filter((o: any) => o.status === 2).length
orderTabs[3].count = allOrders.filter((o: any) => o.status === 3).length
orderTabs[4].count = allOrders.filter((o: any) => o.status === 4).length
orderTabs[5].count = allOrders.filter((o: any) => o.status === 5).length
}
// 生命周期
onLoad((options) => {
if (options['status'] != null) {
const status = options['status'] as string
if (['all', 'pending', 'shipping', 'delivering', 'completed', 'cancelled'].includes(status)) {
activeTab.value = status
}
}
if (options['type'] != null) {
const type = options['type'] as string
if (type === 'pending') activeTab.value = 'pending'
else if (type === 'shipped') activeTab.value = 'delivering' // 映射到待收货
else if (type === 'review') activeTab.value = 'completed' // 映射到已完成
else if (type === 'refund') activeTab.value = 'all' // 申请售后默认显示全部
}
})
const filterOrdersByTab = () => {
const statusMap: Record<string, number> = {
'pending': 1,
'shipping': 2,
'delivering': 3,
'completed': 4,
'cancelled': 5
}
if (activeTab.value === 'all') {
orders.value = allOrdersList.value
} else {
const targetStatus = statusMap[activeTab.value]
orders.value = allOrdersList.value.filter((o: any) => o.status === targetStatus)
}
}
onShow(() => {
loadOrders()
})
const formatDate = (isoString: string): string => {
if (!isoString) return ''
if (isoString == '') return ''
const date = new Date(isoString)
return `${date.getFullYear()}-${(date.getMonth()+1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')} ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`
}
// 辅助函数:获取当前订单数据(必须在 performSearch 之前定义)
function getCurrentOrderData(): OrderItem[] {
return allOrdersList.value
}
// 搜索执行函数(必须在 onSearchInput 等之前定义)
const performSearch = () => {
const keyword = searchKeyword.value.trim().toLowerCase()
if (keyword == '') {
loadOrders()
return
}
// 在当前订单数据中搜索
const allOrders = getCurrentOrderData()
const filtered = allOrders.filter((order: any) => {
const orderObj = order as Record<string, any>
// 搜索订单号
const orderNo = orderObj['order_no'] as string
if (orderNo != null && orderNo.toLowerCase().includes(keyword)) {
return true
}
// 搜索商品名称
const products = orderObj['products']
if (products != null && Array.isArray(products)) {
return products.some((product: any) => {
const productObj = product as Record<string, any>
const name = productObj['name'] as string
return name != null && name.toLowerCase().includes(keyword)
})
}
return false
})
orders.value = filtered
}
// 搜索相关函数
const onSearchInput = (e: any) => {
searchKeyword.value = e.detail.value
const eObj = e as Record<string, any>
const detail = eObj['detail'] as Record<string, any>
searchKeyword.value = detail['value'] as string
performSearch()
}
@@ -339,44 +478,13 @@ const clearSearch = () => {
performSearch()
}
const performSearch = () => {
const keyword = searchKeyword.value.trim().toLowerCase()
if (!keyword) {
loadOrders()
return
}
// 在当前订单数据中搜索
const allOrders = getCurrentOrderData() // 这里需要获取完整的订单数据
const filtered = allOrders.filter((order: any) => {
// 搜索订单号
if (order.order_no && order.order_no.toLowerCase().includes(keyword)) {
return true
}
// 搜索商品名称
if (order.products && Array.isArray(order.products)) {
return order.products.some((product: any) => {
return product.name && product.name.toLowerCase().includes(keyword)
})
}
return false
})
orders.value = filtered
}
const getCurrentOrderData = () => {
return allOrdersList.value
}
const formatSpec = (specs: any): string => {
if (!specs) return ''
if (specs == null) return ''
if (typeof specs === 'string') return specs
if (typeof specs === 'object') {
return Object.keys(specs).map(key => `${key}:${specs[key]}`).join(' ')
return JSON.stringify(specs)
}
return String(specs)
return ''
}
// 切换标签
@@ -387,26 +495,22 @@ const switchTab = (tabId: string) => {
// 获取状态文本
const getStatusText = (status: number): string => {
const statusMap: Record<number, string> = {
1: '待付款',
2: '待货',
3: '待收货',
4: '已完成',
5: '已取消'
}
return statusMap[status] || '未知状态'
if (status == 1) return '待付款'
if (status == 2) return '待发货'
if (status == 3) return '待货'
if (status == 4) return '已完成'
if (status == 5) return '已取消'
return '未知状态'
}
// 获取状态类名
const getStatusClass = (status: number): string => {
const classMap: Record<number, string> = {
1: 'status-pending',
2: 'status-shipping',
3: 'status-delivering',
4: 'status-completed',
5: 'status-cancelled'
}
return classMap[status] || 'status-unknown'
if (status == 1) return 'status-pending'
if (status == 2) return 'status-shipping'
if (status == 3) return 'status-delivering'
if (status == 4) return 'status-completed'
if (status == 5) return 'status-cancelled'
return 'status-unknown'
}
// 下拉刷新
@@ -444,9 +548,13 @@ const cancelOrder = (orderId: string) => {
})
// 更新订单状态
const index = orders.value.findIndex(order => order.id === orderId)
const index = orders.value.findIndex((o: any) => {
const obj = o as Record<string, any>
return obj['id'] === orderId
})
if (index !== -1) {
orders.value[index].status = 5
const orderObj = orders.value[index] as Record<string, any>
orderObj['status'] = 5
orders.value = [...orders.value]
}
}
@@ -473,59 +581,78 @@ const viewLogistics = (orderId: string) => {
})
}
const confirmReceipt = async (orderId: string) => {
uni.showModal({
title: '确认收货',
content: '请确认您已收到商品,且商品无误',
success: async (res) => {
if (res.confirm) {
uni.showLoading({ title: '处理中...' })
try {
const result = await supabaseService.confirmReceipt(orderId)
uni.hideLoading()
if (result.success) {
uni.showToast({
title: '收货成功',
icon: 'success'
})
// 更新本地状态
const index = orders.value.findIndex(order => order.id === orderId)
if (index !== -1) {
orders.value[index].status = 4
orders.value = [...orders.value]
}
// 跳转到评价页面
setTimeout(() => {
const order = orders.value.find(o => o.id === orderId)
if (order) {
goReview(order)
}
}, 1000)
} else {
uni.showToast({
title: result.error || '确认收货失败',
icon: 'none'
})
}
} catch (e) {
uni.hideLoading()
uni.showToast({
title: '系统异常',
icon: 'none'
})
}
}
}
// goReview 必须在 doConfirmReceipt 之前定义,因为 doConfirmReceipt 会调用它
const goReview = (order: any) => {
const orderObj = order as Record<string, any>
const products = orderObj['products'] as any[]
const productIds = products.map((p: any) => {
const pObj = p as Record<string, any>
const pid = pObj['id']
return pid != null ? pid as string : ''
}).join(',')
const orderId = orderObj['id'] as string
uni.navigateTo({
url: `/pages/mall/consumer/review?orderId=${orderId}&productIds=${productIds}`
})
}
const goReview = (order: any) => {
const productIds = order.products.map((p: any) => p.id).join(',')
uni.navigateTo({
url: `/pages/mall/consumer/review?orderId=${order.id}&productIds=${productIds}`
const doConfirmReceipt = async (orderId: string) => {
uni.showLoading({ title: '处理中...' })
try {
const result = await supabaseService.confirmReceipt(orderId)
uni.hideLoading()
if (result.success) {
uni.showToast({
title: '收货成功',
icon: 'success'
})
// 更新本地状态
const index = orders.value.findIndex((o: any) => {
const obj = o as Record<string, any>
return obj['id'] === orderId
})
if (index !== -1) {
const orderObj = orders.value[index] as Record<string, any>
orderObj['status'] = 4
orders.value = [...orders.value]
}
// 跳转到评价页面
setTimeout(() => {
const order = orders.value.find((o: any) => {
const obj = o as Record<string, any>
return obj['id'] === orderId
})
if (order != null) {
goReview(order)
}
}, 1000)
} else {
uni.showToast({
title: result.error ?? '确认收货失败',
icon: 'none'
})
}
} catch (e) {
uni.hideLoading()
uni.showToast({
title: '系统异常',
icon: 'none'
})
}
}
const confirmReceipt = (orderId: string) => {
uni.showModal({
title: '确认收货',
content: '请确认您已收到商品,且商品无误',
success: (res) => {
if (res.confirm) {
doConfirmReceipt(orderId)
}
}
})
}
@@ -552,8 +679,10 @@ const viewOrderDetail = (orderId: string) => {
}
const onApplyRefund = (order: any) => {
const orderObj = order as Record<string, any>
const orderId = orderObj['id']
uni.navigateTo({
url: `/pages/mall/consumer/apply-refund?orderId=${order.id}`
url: `/pages/mall/consumer/apply-refund?orderId=${orderId}`
})
}
@@ -563,7 +692,9 @@ const navigateToSearch = () => {
}
const navigateToProduct = (product: any) => {
uni.navigateTo({ url: `/pages/mall/consumer/product-detail?id=${product.id}` })
const productObj = product as Record<string, any>
const productId = productObj['id']
uni.navigateTo({ url: `/pages/mall/consumer/product-detail?id=${productId}` })
}
const goShopping = () => {
@@ -574,7 +705,9 @@ const goShopping = () => {
<style>
.orders-page {
width: 100%;
min-height: 100vh;
flex: 1;
display: flex;
flex-direction: column;
background-color: #f5f5f5;
}
@@ -586,8 +719,8 @@ const goShopping = () => {
align-items: center;
justify-content: center;
border-bottom: 1px solid #eee;
position: sticky;
top: 0;
/* position: sticky; removed */
/* top: 0; removed */
z-index: 10;
}
@@ -614,9 +747,7 @@ const goShopping = () => {
color: #999;
font-size: 12px;
}
.search-input:focus {
outline: none;
border-color: #ff5000;
background-color: white;
}
@@ -637,17 +768,17 @@ const goShopping = () => {
height: 20px;
line-height: 18px;
text-align: center;
border-radius: 50%;
border-radius: 10px; /* fixed 50% */
background-color: #ddd;
cursor: pointer;
/* cursor: pointer; removed */
}
/* 标签页 */
.order-tabs {
background-color: #ffffff;
border-bottom: 1px solid #e5e5e5;
position: sticky;
top: 50px;
/* position: sticky; removed */
/* top: 50px; removed */
z-index: 10;
}
@@ -660,9 +791,8 @@ const goShopping = () => {
display: flex;
flex-direction: row;
padding: 0 10px;
/* 关键:确保宽度包含所有子元素,允许滚动 */
width: max-content;
min-width: 100%;
/* width: max-content; removed */
/* min-width: 100%; removed */
}
.tab-item {
@@ -683,7 +813,17 @@ const goShopping = () => {
}
.tab-item.active::after {
content: '';
/* content: ''; removed */
/* content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 2px;
background-color: #ff5000; */
}
.active-indicator {
position: absolute;
bottom: 0;
left: 0;
@@ -709,7 +849,7 @@ const goShopping = () => {
/* 内容区 */
.orders-content {
height: calc(100vh - 100px);
flex: 1;
}
/* 空状态 */
@@ -830,7 +970,6 @@ const goShopping = () => {
font-size: 15px;
color: #333;
margin-bottom: 5px;
display: block;
line-height: 1.4;
}
@@ -838,7 +977,6 @@ const goShopping = () => {
font-size: 13px;
color: #999;
margin-bottom: 10px;
display: block;
}
.product-footer {
@@ -906,7 +1044,7 @@ const goShopping = () => {
.action-buttons {
display: flex;
justify-content: flex-end;
gap: 10px;
/* gap: 10px; removed */
}
.action-btn {
@@ -914,7 +1052,8 @@ const goShopping = () => {
border-radius: 15px;
font-size: 13px;
border: 1px solid;
background: none;
background-color: transparent; /* fixed background: none */
margin-left: 10px; /* alternative to gap */
}
.action-btn.cancel {
@@ -966,16 +1105,10 @@ const goShopping = () => {
height: 24px;
border: 2px solid #f0f5ff;
border-top-color: #ff5000;
border-radius: 50%;
animation: spin 1s linear infinite;
border-radius: 12px;
margin-bottom: 10px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.no-more {
text-align: center;
color: #999;