consumer模块完成度95%,完成部署消费者端(外网可访问consumer.meitizs.com),消费者小程序能正常运行在微信开发者工具上
This commit is contained in:
@@ -406,7 +406,7 @@ const deleteAddress = () => {
|
||||
.form-group {
|
||||
background-color: #ffffff;
|
||||
border-radius: 12px;
|
||||
padding: 0 16px;
|
||||
padding: 16px; /* 给整个组增加内边距 */
|
||||
margin-bottom: 12px;
|
||||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.02);
|
||||
}
|
||||
@@ -415,46 +415,62 @@ const deleteAddress = () => {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 18px 0;
|
||||
border-bottom: 1px solid #f9f9f9;
|
||||
padding: 0 16px;
|
||||
min-height: 52px;
|
||||
background-color: #f8f8f8;
|
||||
border-radius: 26px; /* 增加大圆角,使其从直角变为圆角 */
|
||||
margin-bottom: 12px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.form-item:last-child {
|
||||
border-bottom: none;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
padding: 16px;
|
||||
border-radius: 16px; /* 详细地址区域也增加圆角 */
|
||||
}
|
||||
|
||||
.detail-item .label {
|
||||
margin-bottom: 12px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.label {
|
||||
width: 80px;
|
||||
font-size: 15px;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
color: #666;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.input {
|
||||
flex: 1;
|
||||
font-size: 15px;
|
||||
height: 44px; /* 增加高度 */
|
||||
line-height: 44px;
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
padding: 0 4px;
|
||||
background-color: transparent; /* 确保输入框背景透明 */
|
||||
border: none; /* 强制去除安卓原生边框 */
|
||||
outline: none; /* 强制去除焦点边框 */
|
||||
}
|
||||
|
||||
.textarea {
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
height: 80px;
|
||||
font-size: 15px;
|
||||
line-height: 1.5;
|
||||
line-height: 1.6;
|
||||
color: #333;
|
||||
padding: 4px 0;
|
||||
background-color: transparent;
|
||||
border: none; /* 强制去除安卓原生边框 */
|
||||
outline: none; /* 强制去除焦点边框 */
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
color: #ccc;
|
||||
color: #bbb;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
@@ -481,10 +497,11 @@ const deleteAddress = () => {
|
||||
}
|
||||
|
||||
.tag-item {
|
||||
padding: 6px 18px;
|
||||
padding: 8px 20px; /* 增大点击区域 */
|
||||
background-color: #f7f7f7;
|
||||
border-radius: 20px;
|
||||
margin-right: 12px;
|
||||
margin-bottom: 8px; /* 增加底部间距 */
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
@@ -494,7 +511,7 @@ const deleteAddress = () => {
|
||||
}
|
||||
|
||||
.tag-text {
|
||||
font-size: 13px;
|
||||
font-size: 14px; /* 增大标签文字 */
|
||||
color: #666;
|
||||
}
|
||||
|
||||
@@ -506,6 +523,7 @@ const deleteAddress = () => {
|
||||
/* 开关项 */
|
||||
.switch-item {
|
||||
justify-content: space-between;
|
||||
min-height: 72px; /* 增加开关项高度 */
|
||||
}
|
||||
|
||||
.switch-label-group {
|
||||
@@ -514,9 +532,9 @@ const deleteAddress = () => {
|
||||
}
|
||||
|
||||
.sub-label {
|
||||
font-size: 12px;
|
||||
font-size: 13px; /* 增大副标题 */
|
||||
color: #999;
|
||||
margin-top: 4px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
/* 智能填写 */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -433,6 +433,8 @@ const sendMessage = async () => {
|
||||
|
||||
// 清空输入框
|
||||
inputMessage.value = ''
|
||||
// 发送消息时确保收起表情面板
|
||||
showEmoji.value = false
|
||||
|
||||
// 发送到 Supabase
|
||||
if (merchantId.value != '') {
|
||||
@@ -461,12 +463,17 @@ const simulateCustomerReply = async () => {
|
||||
// 插入表情
|
||||
function insertEmoji(emoji: string): void {
|
||||
inputMessage.value += emoji
|
||||
showEmoji.value = false // 选中表情后收起表情列表
|
||||
inputFocus.value = true
|
||||
}
|
||||
|
||||
// 显示表情选择器
|
||||
function showEmojiPicker(): void {
|
||||
showEmoji.value = !showEmoji.value
|
||||
if (showEmoji.value) {
|
||||
// 如果打开表情,通常需要收起键盘
|
||||
uni.hideKeyboard()
|
||||
}
|
||||
}
|
||||
|
||||
// 显示图片选择器
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<!-- 结算页面 -->
|
||||
<template>
|
||||
<view class="checkout-page">
|
||||
<scroll-view class="checkout-content" scroll-y>
|
||||
<scroll-view class="checkout-content" direction="vertical">
|
||||
<!-- 收货地址 -->
|
||||
<view class="address-section" @click="selectAddress">
|
||||
<view v-if="selectedAddress" class="address-info">
|
||||
@@ -137,7 +137,7 @@
|
||||
<text class="popup-close" @click="showAddressPopup = false">×</text>
|
||||
</view>
|
||||
|
||||
<scroll-view class="address-list-container" scroll-y="true" :scroll-with-animation="true">
|
||||
<scroll-view class="address-list-container" direction="vertical" :scroll-with-animation="true">
|
||||
<!-- 登录提示 -->
|
||||
<view v-if="isLoggedIn == false" class="login-prompt" @click="goToLogin">
|
||||
<text class="login-prompt-icon">🔒</text>
|
||||
@@ -215,7 +215,7 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<scroll-view class="form-content" scroll-y="true">
|
||||
<scroll-view class="form-content" direction="vertical">
|
||||
<view class="form-section">
|
||||
<view class="form-item">
|
||||
<text class="form-label">收货人</text>
|
||||
@@ -1422,14 +1422,20 @@ const goToLogin = () => {
|
||||
.checkout-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1; /* replace height: 100vh */
|
||||
background-color: #f5f5f5;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: #f5f5f5;
|
||||
overflow: hidden;
|
||||
}
|
||||
/* 顶部栏 */
|
||||
.checkout-header {
|
||||
background-color: #ffffff;
|
||||
padding: 15px;
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
@@ -1441,6 +1447,9 @@ const goToLogin = () => {
|
||||
|
||||
.checkout-content {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
min-height: 0;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.address-section {
|
||||
@@ -1524,6 +1533,7 @@ const goToLogin = () => {
|
||||
.address-popup {
|
||||
background-color: #ffffff;
|
||||
width: 100%;
|
||||
height: 60vh; /* 显式高度保证内部 scroll-view direction="vertical" 生效 */
|
||||
border-radius: 20px 20px 0 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -1619,13 +1629,13 @@ const goToLogin = () => {
|
||||
background-color: #f8f8f8;
|
||||
width: 92%;
|
||||
max-width: 500px;
|
||||
height: 85vh;
|
||||
height: 600px; /* 改用具体的像素高度,Android 端的 scroll-view 计算更稳健 */
|
||||
border-radius: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
z-index: 10000;
|
||||
z-index: 10001;
|
||||
}
|
||||
|
||||
.form-header {
|
||||
@@ -1646,24 +1656,14 @@ const goToLogin = () => {
|
||||
|
||||
.form-content {
|
||||
flex: 1;
|
||||
height: 100px; /* 进一步减小初始高度,确保安卓端 flex:1 接管 */
|
||||
min-height: 0;
|
||||
width: 100%;
|
||||
/* 在 Android 下,scroll-view 如果不给明确的高度且处于 flex 容器,必须通过 flex-grow 撑开 */
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
background-color: #ffffff;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.address-form-popup {
|
||||
background-color: #f8f8f8;
|
||||
width: 92%;
|
||||
max-width: 500px;
|
||||
height: 80vh; /* 稍微压低高度确保不在边缘产生冲突 */
|
||||
border-radius: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
z-index: 10001;
|
||||
}
|
||||
|
||||
.form-section {
|
||||
background-color: #ffffff;
|
||||
border-radius: 12px;
|
||||
|
||||
@@ -69,7 +69,7 @@ onMounted(() => {
|
||||
|
||||
const useCoupon = (coupon: Coupon) => {
|
||||
uni.switchTab({
|
||||
url: '/pages/mall/consumer/index'
|
||||
url: '/pages/main/index'
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -318,7 +318,7 @@ const addToCart = async (product: FavoriteType) => {
|
||||
|
||||
const goShopping = () => {
|
||||
uni.switchTab({
|
||||
url: '/pages/mall/consumer/index'
|
||||
url: '/pages/main/index'
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -366,7 +366,7 @@ const loadMore = () => {
|
||||
|
||||
const goShopping = () => {
|
||||
uni.switchTab({
|
||||
url: '/pages/mall/consumer/index'
|
||||
url: '/pages/main/index'
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -622,7 +622,7 @@ const rePurchase = async () => {
|
||||
if (successCount > 0) {
|
||||
uni.showToast({ title: '已加入购物车' })
|
||||
setTimeout(() => {
|
||||
uni.switchTab({ url: '/pages/mall/consumer/cart' })
|
||||
uni.switchTab({ url: '/pages/main/cart' })
|
||||
}, 1000)
|
||||
} else {
|
||||
uni.showToast({ title: '操作失败', icon: 'none' })
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<template>
|
||||
<view class="orders-page">
|
||||
<!-- 顶部标题栏 -->
|
||||
<view class="orders-header">
|
||||
<view class="orders-header" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||
<view class="header-search full-width">
|
||||
<input
|
||||
class="search-input"
|
||||
@@ -44,7 +44,7 @@
|
||||
|
||||
<!-- 订单列表 -->
|
||||
<scroll-view
|
||||
scroll-y
|
||||
direction="vertical"
|
||||
class="orders-content"
|
||||
refresher-enabled
|
||||
:refresher-triggered="refreshing"
|
||||
@@ -125,29 +125,30 @@
|
||||
</view>
|
||||
|
||||
<view v-if="order.status === 2" class="action-buttons">
|
||||
<button class="action-btn remind" @click="remindShipping(order.id)">提醒发货</button>
|
||||
<button class="action-btn remind" @click.stop="remindShipping(order.id)">提醒发货</button>
|
||||
<button class="action-btn refund" @click.stop="onApplyRefund(order)">申请售后</button>
|
||||
</view>
|
||||
|
||||
<view v-if="order.status === 3" class="action-buttons">
|
||||
<button class="action-btn view" @click="viewLogistics(order.id)">查看物流</button>
|
||||
<button class="action-btn confirm" @click="confirmReceipt(order.id)">确认收货</button>
|
||||
<button class="action-btn view" @click.stop="viewLogistics(order.id)">查看物流</button>
|
||||
<button class="action-btn confirm" @click.stop="confirmReceipt(order.id)">确认收货</button>
|
||||
<button class="action-btn refund" @click.stop="onApplyRefund(order)">申请售后</button>
|
||||
</view>
|
||||
|
||||
<view v-if="order.status === 4" class="action-buttons">
|
||||
<button class="action-btn review" @click="goReview(order)">评价</button>
|
||||
<button class="action-btn review" @click.stop="goReview(order)">评价</button>
|
||||
<button class="action-btn refund" @click.stop="onApplyRefund(order)">申请售后</button>
|
||||
<button class="action-btn repurchase" @click="repurchase(order)">再次购买</button>
|
||||
<button class="action-btn repurchase" @click.stop="repurchase(order)">再次购买</button>
|
||||
</view>
|
||||
|
||||
<view v-if="order.status === 5" class="action-buttons">
|
||||
<button class="action-btn view" @click="viewOrderDetail(order.id)">查看详情</button>
|
||||
<button class="action-btn view" @click.stop="viewOrderDetail(order.id)">查看详情</button>
|
||||
</view>
|
||||
|
||||
<view v-if="order.status === 6" class="action-buttons">
|
||||
<button class="action-btn view" @click="viewOrderDetail(order.id)">查看详情</button>
|
||||
<button class="action-btn refund" @click="viewRefundProgress(order.id)">退款进度</button>
|
||||
<button class="action-btn view" @click.stop="viewOrderDetail(order.id)">查看详情</button>
|
||||
<button class="action-btn cancel" @click.stop="cancelRefund(order.id)">取消退款</button>
|
||||
<button class="action-btn refund" @click.stop="viewRefundProgress(order.id)">退款进度</button>
|
||||
</view>
|
||||
|
||||
<view v-if="order.status === 7" class="action-buttons">
|
||||
@@ -222,6 +223,7 @@ const hasMore = ref<boolean>(true)
|
||||
const refreshing = ref<boolean>(false)
|
||||
const page = ref<number>(1)
|
||||
const activeTab = ref<string>('all')
|
||||
const statusBarHeight = ref<number>(0)
|
||||
const searchKeyword = ref<string>('')
|
||||
|
||||
// 订单标签页 - 使用 ref 以便整体替换
|
||||
@@ -504,6 +506,10 @@ const loadOrders = async () => {
|
||||
|
||||
// 生命周期
|
||||
onLoad((options) => {
|
||||
// 初始化状态栏高度
|
||||
const systemInfo = uni.getSystemInfoSync()
|
||||
statusBarHeight.value = systemInfo.statusBarHeight ?? 0
|
||||
|
||||
if (options == null) return
|
||||
const statusVal = options['status']
|
||||
if (statusVal != null) {
|
||||
@@ -518,7 +524,7 @@ onLoad((options) => {
|
||||
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' // 申请售后默认显示全部
|
||||
else if (type === 'refund') activeTab.value = 'aftersale' // 退款/售后跳转到售后标签页
|
||||
}
|
||||
})
|
||||
|
||||
@@ -771,15 +777,11 @@ const viewLogistics = (orderId: string) => {
|
||||
}
|
||||
|
||||
// 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 : ''
|
||||
const goReview = (order: OrderItem) => {
|
||||
const productIds = order.products.map((p: OrderProduct): string => {
|
||||
return p.id
|
||||
}).join(',')
|
||||
const orderId = orderObj['id'] as string
|
||||
const orderId = order.id
|
||||
uni.navigateTo({
|
||||
url: `/pages/mall/consumer/review?orderId=${orderId}&productIds=${productIds}`
|
||||
})
|
||||
@@ -798,22 +800,15 @@ const doConfirmReceipt = async (orderId: string) => {
|
||||
})
|
||||
|
||||
// 更新本地状态
|
||||
const index = orders.value.findIndex((o: any) => {
|
||||
const obj = o as Record<string, any>
|
||||
return obj['id'] === orderId
|
||||
})
|
||||
const index = orders.value.findIndex((o: OrderItem): boolean => o.id === orderId)
|
||||
if (index !== -1) {
|
||||
const orderObj = orders.value[index] as Record<string, any>
|
||||
orderObj['status'] = 4
|
||||
orders.value[index].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
|
||||
})
|
||||
const order = orders.value.find((o: OrderItem): boolean => o.id === orderId)
|
||||
if (order != null) {
|
||||
goReview(order)
|
||||
}
|
||||
@@ -845,11 +840,10 @@ const confirmReceipt = (orderId: string) => {
|
||||
})
|
||||
}
|
||||
|
||||
const repurchase = (order: any) => {
|
||||
const orderObj = order as Record<string, any>
|
||||
const products = orderObj['products'] as any[]
|
||||
const repurchase = (order: OrderItem) => {
|
||||
const products = order.products
|
||||
|
||||
if (products == null || products.length === 0) {
|
||||
if (products.length === 0) {
|
||||
uni.showToast({
|
||||
title: '订单无商品',
|
||||
icon: 'none'
|
||||
@@ -864,12 +858,12 @@ const repurchase = (order: any) => {
|
||||
let successCount = 0
|
||||
|
||||
for (let i = 0; i < products.length; i++) {
|
||||
const pObj = products[i] as Record<string, any>
|
||||
const productId = pObj['id'] as string
|
||||
const merchantId = orderObj['merchant_id'] as string
|
||||
const product = products[i]
|
||||
const productId = product.id
|
||||
const merchantId = order.merchant_id
|
||||
|
||||
if (productId != null && productId !== '') {
|
||||
supabaseService.addToCart(productId, 1, '', merchantId ?? '').then((success) => {
|
||||
supabaseService.addToCart(productId, 1, '', merchantId ?? '').then((success: boolean) => {
|
||||
completed++
|
||||
if (success) successCount++
|
||||
if (completed === total) {
|
||||
@@ -929,9 +923,8 @@ const viewOrderDetail = (orderId: string) => {
|
||||
})
|
||||
}
|
||||
|
||||
const onApplyRefund = (order: any) => {
|
||||
const orderObj = order as Record<string, any>
|
||||
const orderId = orderObj['id']
|
||||
const onApplyRefund = (order: OrderItem) => {
|
||||
const orderId = order.id
|
||||
uni.navigateTo({
|
||||
url: `/pages/mall/consumer/apply-refund?orderId=${orderId}`
|
||||
})
|
||||
@@ -943,6 +936,32 @@ const viewRefundProgress = (orderId: string) => {
|
||||
})
|
||||
}
|
||||
|
||||
const doCancelRefund = async (orderId: string) => {
|
||||
uni.showLoading({ title: '处理中...' })
|
||||
const result = await supabaseService.cancelRefund(orderId)
|
||||
uni.hideLoading()
|
||||
|
||||
if (result.success) {
|
||||
uni.showToast({ title: '已取消退款', icon: 'success' })
|
||||
loadOrders()
|
||||
} else {
|
||||
uni.showToast({ title: result.message, icon: 'none' })
|
||||
}
|
||||
}
|
||||
|
||||
// 取消退款申请
|
||||
const cancelRefund = (orderId: string) => {
|
||||
uni.showModal({
|
||||
title: '确认取消',
|
||||
content: '确定要取消退款申请吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
doCancelRefund(orderId)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 处理订单操作
|
||||
const handleOrderAction = (order: OrderItem, action: string) => {
|
||||
if (action === '取消订单') {
|
||||
@@ -963,6 +982,8 @@ const handleOrderAction = (order: OrderItem, action: string) => {
|
||||
deleteOrder(order.id)
|
||||
} else if (action === '退款进度') {
|
||||
viewRefundProgress(order.id)
|
||||
} else if (action === '取消退款') {
|
||||
cancelRefund(order.id)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -982,7 +1003,7 @@ const showOrderMenu = (order: OrderItem) => {
|
||||
} else if (status === 5) {
|
||||
actions = ['删除订单', '再次购买', '联系卖家']
|
||||
} else if (status === 6) {
|
||||
actions = ['退款进度', '联系卖家']
|
||||
actions = ['取消退款', '退款进度', '联系卖家']
|
||||
} else if (status === 7) {
|
||||
actions = ['再次购买', '联系卖家']
|
||||
}
|
||||
@@ -1001,59 +1022,75 @@ const navigateToSearch = () => {
|
||||
uni.navigateTo({ url: '/pages/mall/consumer/search' })
|
||||
}
|
||||
|
||||
const navigateToProduct = (product: any) => {
|
||||
const productObj = product as Record<string, any>
|
||||
const productId = productObj['id']
|
||||
const navigateToProduct = (product: OrderProduct) => {
|
||||
const productId = product.id
|
||||
uni.navigateTo({ url: `/pages/mall/consumer/product-detail?id=${productId}` })
|
||||
}
|
||||
|
||||
const goShopping = () => {
|
||||
uni.switchTab({ url: '/pages/mall/consumer/index' })
|
||||
uni.switchTab({ url: '/pages/main/index' })
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.orders-page {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
/* 采用相对于窗口的 100% 方案 */
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
/* App/小程序通过 fixed 解决根容器撑满问题,H5/电脑端建议使用 flex 1 */
|
||||
/* #ifdef APP-PLUS || MP-WEIXIN */
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
/* #endif */
|
||||
/* #ifdef H5 */
|
||||
position: relative;
|
||||
/* #endif */
|
||||
background-color: #f5f5f5;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 头部 */
|
||||
/* 头部:确保显式可见且不被遮挡 */
|
||||
.orders-header {
|
||||
background-color: white;
|
||||
padding: 15px;
|
||||
background-color: #ffffff;
|
||||
padding: 10px 15px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-bottom: 1px solid #eee;
|
||||
z-index: 10;
|
||||
width: 100%;
|
||||
border-bottom: 1px solid #eeeeee;
|
||||
z-index: 999;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.header-search.full-width {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
flex: 1;
|
||||
height: 36px;
|
||||
border: 1px solid #ddd;
|
||||
line-height: normal;
|
||||
border: 1px solid #dddddd;
|
||||
border-radius: 18px;
|
||||
padding: 0 40px 0 16px;
|
||||
font-size: 14px;
|
||||
background-color: #f5f5f5;
|
||||
color: #333;
|
||||
color: #333333;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.search-input::placeholder {
|
||||
@@ -1086,7 +1123,7 @@ const goShopping = () => {
|
||||
/* cursor: pointer; removed */
|
||||
}
|
||||
|
||||
/* 标签页 */
|
||||
/* 标签页容器:确保在安卓下层级正确且不随内容滚动 */
|
||||
.order-tabs-fixed-container {
|
||||
background-color: #ffffff;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
@@ -1094,26 +1131,25 @@ const goShopping = () => {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
z-index: 10;
|
||||
height: 50px;
|
||||
z-index: 100 !important;
|
||||
flex-shrink: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* 安卓端滚动修复 */
|
||||
/* 安卓端滚动修复:关键容器 */
|
||||
.orders-content {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
height: 0; /* 关键:强制 flex: 1 生效 */
|
||||
/* 极致方案:不再设置 height: 0,改用 flex: 1 填满,并显式设置 scroll-y 的表现 */
|
||||
min-height: 0;
|
||||
flex-shrink: 1;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.orders-header {
|
||||
background-color: white;
|
||||
padding: 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-bottom: 1px solid #eee;
|
||||
/* 顶部内容区域必须显式不可伸缩,防止撑占滚动空间 */
|
||||
.orders-header, .order-tabs-fixed-container {
|
||||
flex-shrink: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.tab-item-fixed {
|
||||
@@ -1456,6 +1492,16 @@ const goShopping = () => {
|
||||
border-color: #34c759;
|
||||
}
|
||||
|
||||
.action-btn.refund {
|
||||
color: #666;
|
||||
border-color: #ccc;
|
||||
}
|
||||
|
||||
.action-btn.cancel {
|
||||
color: #ff6b6b;
|
||||
border-color: #ff6b6b;
|
||||
}
|
||||
|
||||
.action-btn.review {
|
||||
color: #ff9500;
|
||||
border-color: #ff9500;
|
||||
|
||||
@@ -95,7 +95,7 @@ const viewOrder = () => {
|
||||
|
||||
const goHome = () => {
|
||||
uni.switchTab({
|
||||
url: '/pages/mall/consumer/index'
|
||||
url: '/pages/main/index'
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<!-- 支付页面 -->
|
||||
<template>
|
||||
<view class="payment-page">
|
||||
<view class="payment-content">
|
||||
<scroll-view class="payment-content" direction="vertical">
|
||||
<!-- 价格明细 -->
|
||||
<view class="price-detail-section">
|
||||
<text class="section-title">价格明细</text>
|
||||
@@ -71,7 +71,7 @@
|
||||
</view>
|
||||
<text class="forgot-password" @click="forgotPassword">忘记密码?</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 底部支付按钮 -->
|
||||
<view class="payment-bottom">
|
||||
@@ -614,8 +614,15 @@ onUnmounted(() => {
|
||||
.payment-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #f5f5f5;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.payment-header {
|
||||
|
||||
@@ -832,7 +832,7 @@ export default {
|
||||
},
|
||||
|
||||
goToCart() {
|
||||
uni.switchTab({ url: '/pages/mall/consumer/cart' })
|
||||
uni.switchTab({ url: '/pages/main/cart' })
|
||||
},
|
||||
|
||||
decreaseQuantity() {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -107,7 +107,7 @@ onMounted(() => {
|
||||
|
||||
const usePacket = (item: RedPacket) => {
|
||||
uni.switchTab({
|
||||
url: '/pages/mall/consumer/index'
|
||||
url: '/pages/main/index'
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
<!-- 评价页面 -->
|
||||
<template>
|
||||
<view class="review-page">
|
||||
<!-- 顶部栏 -->
|
||||
<!-- 顶部栏:已移除“发表评价”文字 -->
|
||||
<view class="review-header">
|
||||
<text class="back-btn" @click="goBack">‹</text>
|
||||
<text class="header-title">评价商品</text>
|
||||
<view class="header-back" @click="goBack">
|
||||
<image class="back-icon" src="/static/icons/back.png" mode="aspectFit"></image>
|
||||
</view>
|
||||
<view class="header-title-placeholder"></view>
|
||||
<view class="header-right"></view>
|
||||
</view>
|
||||
|
||||
<scroll-view class="review-content" direction="vertical">
|
||||
@@ -145,7 +148,6 @@
|
||||
<script setup lang="uts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import supa from '@/components/supadb/aksupainstance.uts'
|
||||
import { supabaseService } from '@/utils/supabaseService.uts'
|
||||
|
||||
type OrderType = {
|
||||
@@ -195,72 +197,58 @@ const isSubmitting = ref<boolean>(false)
|
||||
|
||||
const loadOrderData = async (): Promise<void> => {
|
||||
try {
|
||||
const orderRes = await supa
|
||||
.from('ml_orders')
|
||||
.select('*')
|
||||
.eq('id', orderId.value)
|
||||
.single()
|
||||
.execute()
|
||||
|
||||
if (orderRes.error != null) {
|
||||
console.error('加载订单失败:', orderRes.error)
|
||||
return
|
||||
}
|
||||
|
||||
if (orderRes.data != null) {
|
||||
const orderData = orderRes.data as UTSJSONObject
|
||||
order.value = {
|
||||
id: orderData.getString('id') ?? '',
|
||||
order_no: orderData.getString('order_no') ?? '',
|
||||
created_at: orderData.getString('created_at') ?? '',
|
||||
merchant_id: orderData.getString('merchant_id') ?? ''
|
||||
} as OrderType
|
||||
}
|
||||
|
||||
const itemsRes = await supa
|
||||
.from('ml_order_items')
|
||||
.select(`
|
||||
*,
|
||||
product:product_id(images)
|
||||
`)
|
||||
.eq('order_id', orderId.value)
|
||||
.execute()
|
||||
|
||||
if (itemsRes.error != null) {
|
||||
console.error('加载订单商品失败:', itemsRes.error)
|
||||
return
|
||||
}
|
||||
|
||||
const rawData = itemsRes.data
|
||||
let itemsArray: Array<any> = []
|
||||
if (rawData != null) {
|
||||
itemsArray = rawData as Array<any>
|
||||
}
|
||||
console.log('[loadOrderData] 开始加载订单数据, orderId:', orderId.value)
|
||||
|
||||
const processedItems: Array<OrderItemType> = []
|
||||
for (let i: number = 0; i < itemsArray.length; i++) {
|
||||
const item = itemsArray[i] as UTSJSONObject
|
||||
const productObjRaw = item.get('product')
|
||||
const productObj = (productObjRaw != null) ? (productObjRaw as UTSJSONObject) : null
|
||||
const imagesArrRaw = (productObj != null) ? productObj.get('images') : null
|
||||
const imagesArr = (imagesArrRaw != null) ? (imagesArrRaw as Array<string>) : []
|
||||
const firstImage = (imagesArr.length > 0) ? imagesArr[0] : '/static/default-product.png'
|
||||
const skuSpecRaw = item.get('sku_specifications')
|
||||
const skuSpec = (skuSpecRaw != null) ? (skuSpecRaw as any) : null
|
||||
const processedItem: OrderItemType = {
|
||||
id: (item.getNumber('id') ?? 0) as number,
|
||||
order_id: (item.getNumber('order_id') ?? 0) as number,
|
||||
product_id: (item.getNumber('product_id') ?? 0) as number,
|
||||
product_name: item.getString('product_name') ?? '',
|
||||
price: (item.getNumber('price') ?? 0) as number,
|
||||
quantity: (item.getNumber('quantity') ?? 1) as number,
|
||||
sku_specifications: skuSpec,
|
||||
product_image: firstImage
|
||||
}
|
||||
processedItems.push(processedItem)
|
||||
// 使用 supabaseService 获取订单详情
|
||||
const orderDetailRaw = await supabaseService.getOrderDetail(orderId.value)
|
||||
console.log('[loadOrderData] orderDetailRaw:', JSON.stringify(orderDetailRaw))
|
||||
|
||||
if (orderDetailRaw == null) {
|
||||
console.error('加载订单失败: 未找到订单')
|
||||
uni.showToast({ title: '订单不存在', icon: 'none' })
|
||||
return
|
||||
}
|
||||
orderItems.value = processedItems
|
||||
|
||||
// 转换为 UTSJSONObject
|
||||
const orderDetail = JSON.parse(JSON.stringify(orderDetailRaw)) as UTSJSONObject
|
||||
|
||||
// 解析订单基本信息
|
||||
order.value = {
|
||||
id: orderDetail.getString('id') ?? '',
|
||||
order_no: orderDetail.getString('order_no') ?? '',
|
||||
created_at: orderDetail.getString('created_at') ?? '',
|
||||
merchant_id: orderDetail.getString('merchant_id') ?? ''
|
||||
} as OrderType
|
||||
|
||||
// 解析订单商品
|
||||
const itemsRaw = orderDetail.get('ml_order_items')
|
||||
console.log('[loadOrderData] itemsRaw:', JSON.stringify(itemsRaw))
|
||||
|
||||
if (itemsRaw != null) {
|
||||
const itemsList = itemsRaw as any[]
|
||||
const processedItems: Array<OrderItemType> = []
|
||||
|
||||
for (let i: number = 0; i < itemsList.length; i++) {
|
||||
const itemStr = JSON.stringify(itemsList[i])
|
||||
const item = JSON.parse(itemStr) as UTSJSONObject
|
||||
const skuSpec = item.get('sku_specifications')
|
||||
|
||||
processedItems.push({
|
||||
id: (item.getNumber('id') ?? 0) as number,
|
||||
order_id: (item.getNumber('order_id') ?? 0) as number,
|
||||
product_id: (item.getNumber('product_id') ?? 0) as number,
|
||||
product_name: item.getString('product_name') ?? '',
|
||||
price: (item.getNumber('price') ?? 0) as number,
|
||||
quantity: (item.getNumber('quantity') ?? 1) as number,
|
||||
sku_specifications: skuSpec,
|
||||
product_image: item.getString('product_image') ?? item.getString('image_url') ?? '/static/default-product.png'
|
||||
} as OrderItemType)
|
||||
}
|
||||
orderItems.value = processedItems
|
||||
console.log('[loadOrderData] processedItems count:', processedItems.length)
|
||||
}
|
||||
|
||||
// 初始化评价数据
|
||||
const count = orderItems.value.length
|
||||
const newRatings: Array<number> = []
|
||||
const newContents: Array<string> = []
|
||||
@@ -274,23 +262,25 @@ const loadOrderData = async (): Promise<void> => {
|
||||
contents.value = newContents
|
||||
images.value = newImages
|
||||
|
||||
const orderObj = order.value as UTSJSONObject
|
||||
const merchantId = orderObj.getString('merchant_id')
|
||||
if (merchantId != null && merchantId !== '') {
|
||||
const merchantRes = await supa
|
||||
.from('ml_shops')
|
||||
.select('id, shop_name, rating')
|
||||
.eq('id', merchantId)
|
||||
.single()
|
||||
.execute()
|
||||
|
||||
if (merchantRes.error == null && merchantRes.data != null) {
|
||||
merchant.value = merchantRes.data as MerchantType
|
||||
// 获取商家信息
|
||||
const orderObj = order.value
|
||||
if (orderObj != null) {
|
||||
const merchantId = orderObj.merchant_id
|
||||
if (merchantId != '') {
|
||||
const shopInfo = await supabaseService.getShopByMerchantId(merchantId)
|
||||
if (shopInfo != null) {
|
||||
merchant.value = {
|
||||
id: shopInfo.id,
|
||||
shop_name: shopInfo.shop_name,
|
||||
rating: shopInfo.rating_avg ?? 5
|
||||
} as MerchantType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
console.error('加载订单数据异常:', err)
|
||||
uni.showToast({ title: '加载失败', icon: 'none' })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,10 +312,19 @@ const formatTime = (timeStr?: string): string => {
|
||||
|
||||
const getSpecText = (specs: any | null): string => {
|
||||
if (specs == null) return ''
|
||||
if (specs instanceof UTSJSONObject) {
|
||||
return '规格信息'
|
||||
if (typeof specs === 'string') return specs as string
|
||||
|
||||
try {
|
||||
const specObj = JSON.parse(JSON.stringify(specs)) as UTSJSONObject
|
||||
const jsonStr = JSON.stringify(specObj)
|
||||
if (jsonStr == '{}' || jsonStr == 'null') return ''
|
||||
|
||||
// 简单解析:直接返回 JSON 字符串(去除大括号)
|
||||
const cleanStr = jsonStr.replace(/^\{|\}$/g, '').replace(/"/g, '').replace(/:/g, ': ').replace(/,/g, '; ')
|
||||
return cleanStr
|
||||
} catch (e) {
|
||||
return ''
|
||||
}
|
||||
return specs as string
|
||||
}
|
||||
|
||||
// 获取评分文本
|
||||
@@ -548,23 +547,37 @@ const goBack = (): void => {
|
||||
|
||||
.review-header {
|
||||
background-color: #ffffff;
|
||||
padding: 15px;
|
||||
padding: 10px 15px;
|
||||
height: 44px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.header-back {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.back-btn {
|
||||
font-size: 24px;
|
||||
color: #333333;
|
||||
padding: 5px;
|
||||
margin-right: 15px;
|
||||
.back-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
.header-title-placeholder {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.header-right {
|
||||
width: 44px;
|
||||
}
|
||||
|
||||
.review-content {
|
||||
@@ -632,39 +645,46 @@ const goBack = (): void => {
|
||||
|
||||
.rating-section {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
margin-bottom: 20px;
|
||||
padding: 5px 0;
|
||||
}
|
||||
|
||||
.rating-label {
|
||||
font-size: 14px;
|
||||
font-size: 15px;
|
||||
color: #333333;
|
||||
margin-right: 15px;
|
||||
font-weight: 500;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.rating-stars {
|
||||
display: flex;
|
||||
/* gap: 10px; removed */
|
||||
}
|
||||
|
||||
.rating-stars.small {
|
||||
/* gap: 5px; removed */
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.star-icon {
|
||||
font-size: 24px;
|
||||
color: #cccccc;
|
||||
margin-right: 10px;
|
||||
font-size: 26px;
|
||||
margin-right: 8px;
|
||||
color: #e0e0e0;
|
||||
transition: transform 0.1s ease;
|
||||
}
|
||||
|
||||
.star-icon.active {
|
||||
color: #ff5000;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.rating-text {
|
||||
margin-left: 15px;
|
||||
font-size: 14px;
|
||||
color: #666666;
|
||||
color: #999999;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.rating-stars.small {
|
||||
/* gap: 5px; removed */
|
||||
}
|
||||
|
||||
.content-section {
|
||||
@@ -783,28 +803,49 @@ const goBack = (): void => {
|
||||
|
||||
.merchant-section {
|
||||
background-color: #ffffff;
|
||||
padding: 15px;
|
||||
margin-bottom: 10px;
|
||||
padding: 20px 15px;
|
||||
margin-top: 15px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
/* display: block; removed */
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
font-weight: 600;
|
||||
color: #333333;
|
||||
margin-bottom: 15px;
|
||||
margin-bottom: 20px;
|
||||
padding-left: 10px;
|
||||
border-left: 4px solid #ff5000;
|
||||
}
|
||||
|
||||
.merchant-rating {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
margin-bottom: 20px;
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
.rating-item {
|
||||
font-size: 14px;
|
||||
color: #333333;
|
||||
color: #666666;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.merchant-rating .rating-stars.small {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.merchant-rating .rating-stars.small .star-icon {
|
||||
font-size: 20px;
|
||||
margin-right: 5px;
|
||||
color: #e0e0e0;
|
||||
}
|
||||
|
||||
.merchant-rating .rating-stars.small .star-icon.active {
|
||||
color: #ff5000;
|
||||
}
|
||||
|
||||
.tips-section {
|
||||
|
||||
@@ -821,7 +821,7 @@ const goBack = () => {
|
||||
} else {
|
||||
// 如果只有一页(由于深链接或重定向),返回首页
|
||||
uni.switchTab({
|
||||
url: '/pages/mall/consumer/index'
|
||||
url: '/pages/main/index'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,7 +212,7 @@ onBackPress((options) => {
|
||||
// 无论是什么触发的返回(系统返回键或导航栏返回按钮),都跳转到profile
|
||||
// 注意:onBackPress 只能在 page 中使用,component 中无效
|
||||
uni.switchTab({
|
||||
url: '/pages/mall/consumer/profile'
|
||||
url: '/pages/main/profile'
|
||||
})
|
||||
// 返回 true 表示阻止默认返回行为
|
||||
return true
|
||||
|
||||
@@ -115,7 +115,7 @@ const goToShop = (shop: FollowedShop): void => {
|
||||
}
|
||||
|
||||
const goHome = (): void => {
|
||||
uni.switchTab({ url: '/pages/mall/consumer/index' })
|
||||
uni.switchTab({ url: '/pages/main/index' })
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
@@ -135,7 +135,7 @@ const confirmSubscribe = async () => {
|
||||
if (ins != null && ins.error == null) {
|
||||
uni.showToast({ title: '订阅成功', icon: 'success' })
|
||||
setTimeout(() => {
|
||||
uni.redirectTo({ url: '/pages/mall/consumer/profile' })
|
||||
uni.redirectTo({ url: '/pages/main/profile' })
|
||||
}, 600)
|
||||
} else {
|
||||
uni.showToast({ title: ins?.error?.message ?? '订阅失败', icon: 'none' })
|
||||
|
||||
Reference in New Issue
Block a user