hookehuyr

feat(订单): 添加订单评价功能及支付组件集成

- 新增订单评价弹窗组件,支持评分和评价内容输入
- 实现评价提交逻辑,包含表单验证和状态管理
- 集成支付组件,优化支付流程
- 添加评价样式和响应式布局
- 完善订单列表交互,区分已评价和未评价状态
......@@ -23,6 +23,7 @@ declare module 'vue' {
NutPopup: typeof import('@nutui/nutui-taro')['Popup']
NutRadio: typeof import('@nutui/nutui-taro')['Radio']
NutRadioGroup: typeof import('@nutui/nutui-taro')['RadioGroup']
NutRate: typeof import('@nutui/nutui-taro')['Rate']
NutRow: typeof import('@nutui/nutui-taro')['Row']
NutSearchbar: typeof import('@nutui/nutui-taro')['Searchbar']
NutSticky: typeof import('@nutui/nutui-taro')['Sticky']
......
......@@ -247,6 +247,141 @@
color: #9ca3af;
}
// 评价弹窗样式
.rate-popup {
padding: 40rpx;
height: 100%;
display: flex;
flex-direction: column;
.rate-header {
text-align: center;
margin-bottom: 40rpx;
.rate-title {
font-size: 36rpx;
font-weight: 600;
color: #333;
}
}
.rate-content {
flex: 1;
.product-info {
display: flex;
padding: 30rpx;
background: #f8f9fa;
border-radius: 16rpx;
margin-bottom: 40rpx;
.product-image {
width: 120rpx;
height: 120rpx;
border-radius: 12rpx;
margin-right: 24rpx;
}
.product-details {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
.product-name {
font-size: 32rpx;
font-weight: 600;
color: #333;
margin-bottom: 8rpx;
}
.product-specs {
font-size: 26rpx;
color: #666;
margin-bottom: 8rpx;
}
.product-price {
font-size: 32rpx;
font-weight: 600;
color: #ff6b35;
}
}
}
.rate-score-section {
margin-bottom: 40rpx;
.score-label {
display: block;
font-size: 30rpx;
color: #333;
margin-bottom: 20rpx;
font-weight: 500;
}
.rate-stars {
margin-bottom: 16rpx;
}
.score-text {
font-size: 26rpx;
color: #666;
margin-left: 16rpx;
}
}
.rate-input-section {
.input-label {
display: block;
font-size: 30rpx;
color: #333;
margin-bottom: 20rpx;
font-weight: 500;
}
.rate-textarea {
width: 100%;
min-height: 200rpx;
border: 2rpx solid #e5e5e5;
border-radius: 12rpx;
padding: 20rpx;
font-size: 28rpx;
line-height: 1.5;
&:focus {
border-color: #ff6b35;
}
&.readonly {
background-color: #f8f9fa;
border-color: #e9ecef;
color: #666;
}
}
.review-date {
display: block;
font-size: 24rpx;
color: #999;
margin-top: 16rpx;
text-align: right;
}
}
}
.rate-footer {
padding-top: 40rpx;
.nut-button {
height: 88rpx;
border-radius: 44rpx;
font-size: 32rpx;
font-weight: 600;
}
}
}
/* 响应式适配 */
@media (max-width: 750px) {
.header {
......@@ -305,3 +440,30 @@
gap: 16rpx;
}
}
/* 加载指示器 */
.loading-container {
display: flex;
align-items: center;
justify-content: center;
padding: 40rpx;
gap: 16rpx;
}
.loading-text {
font-size: 28rpx;
color: #9ca3af;
}
:deep(.nut-textarea) {
border: 1rpx solid #e5e7eb;
border-radius: 16rpx;
padding: 24rpx;
}
:deep(.nut-textarea__textarea) {
font-size: 28rpx;
color: #374151;
line-height: 1.5;
}
......
<!--
* @Date: 2022-09-19 14:11:06
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-07-03 15:55:17
* @LastEditTime: 2025-07-03 16:32:53
* @FilePath: /jgdl/src/pages/myOrders/index.vue
* @Description: 订单管理页面
-->
......@@ -9,7 +9,7 @@
<view class="order-management-page">
<nut-sticky>
<!-- 买车/卖车切换 -->
<view class="view-mode-toggle">
<view id="mode-toggle" class="view-mode-toggle">
<view class="toggle-container">
<view
class="toggle-option"
......@@ -29,7 +29,7 @@
</view>
<!-- 状态筛选标签 -->
<view class="status-tabs">
<view id="status-tabs" class="status-tabs">
<view
class="tab-item"
:class="{ active: activeTab === 'all' }"
......@@ -123,7 +123,7 @@
<nut-button
type="primary"
size="small"
@click="handlePayment(order.id)"
@click="handlePayment(order)"
>
去支付
</nut-button>
......@@ -144,7 +144,7 @@
@click="rateOrder(order.id)"
class="ml-2"
>
评价
{{ order.review ? '查看评价' : '评价' }}
</nut-button>
</template>
......@@ -173,6 +173,87 @@
</view>
</scroll-view>
</view>
<!-- 支付组件 -->
<payCard :visible="show_pay" :data="payData" @close="onPayClose" />
<!-- 评价弹窗 -->
<nut-popup
v-model:visible="showRatePopup"
position="bottom"
:style="{ height: '60%' }"
round
closeable
@close="closeRatePopup"
>
<view class="rate-popup">
<view class="rate-header">
<text class="rate-title">评价商品</text>
</view>
<view class="rate-content">
<!-- 商品信息 -->
<view class="product-info" v-if="currentRateOrder">
<image
:src="currentRateOrder.vehicle.imageUrl"
class="product-image"
mode="aspectFill"
/>
<view class="product-details">
<text class="product-name">{{ currentRateOrder.vehicle.name }}</text>
<text class="product-specs">{{ currentRateOrder.vehicle.year }} · {{ currentRateOrder.vehicle.mileage }}</text>
<text class="product-price">¥{{ currentRateOrder.vehicle.price }}</text>
</view>
</view>
<!-- 评分组件 -->
<view class="rate-score-section">
<text class="score-label">{{ isReadOnlyMode ? '评分' : '请给商品评分' }}</text>
<nut-rate
v-model="rateScore"
:readonly="isReadOnlyMode"
:size="isReadOnlyMode ? '20' : '24'"
active-color="#ff6b35"
void-color="#e5e5e5"
class="rate-stars"
/>
<text v-if="isReadOnlyMode" class="score-text">{{ rateScore }}/5分</text>
</view>
<!-- 评价输入框 -->
<view class="rate-input-section">
<text class="input-label">{{ isReadOnlyMode ? '评价内容' : '请输入评价内容' }}</text>
<view class="border border-gray-100">
<nut-textarea
v-model="rateContent"
:placeholder="isReadOnlyMode ? '' : '请输入您的评价内容...'"
:max-length="200"
:rows="4"
:show-word-limit="!isReadOnlyMode"
:readonly="isReadOnlyMode"
:class="{ 'readonly': isReadOnlyMode }"
/>
</view>
<text v-if="isReadOnlyMode && currentRateOrder?.review?.date" class="review-date">
评价时间:{{ currentRateOrder.review.date }}
</text>
</view>
</view>
<!-- 提交按钮 -->
<view class="rate-footer" v-if="!isReadOnlyMode">
<nut-button
type="primary"
size="large"
@click="submitRate"
:loading="submittingRate"
block
>
提交评价
</nut-button>
</view>
</view>
</nut-popup>
</view>
</template>
......@@ -180,6 +261,13 @@
import { ref, computed, onMounted } from 'vue'
import Taro from '@tarojs/taro'
import './index.less'
import { $ } from '@tarojs/extend'
import payCard from '@/components/payCard.vue'
// NutUI组件已全局注册,无需单独导入Rate
const scrollStyle = ref({
height: 'calc(100vh)'
})
// 页面状态
const activeTab = ref('all')
......@@ -187,10 +275,26 @@ const viewMode = ref('bought')
const loading = ref(false)
const hasMore = ref(true)
const show_pay = ref(false)
const payData = ref({
id: '',
price: 0,
remain_time: 0
})
// 评价相关状态
const showRatePopup = ref(false)
const currentRateOrder = ref(null)
const rateContent = ref('')
const rateScore = ref(5)
const submittingRate = ref(false)
const isReadOnlyMode = ref(false)
// 模拟订单数据
const boughtOrders = ref([
{
id: '1',
price: 999,
date: '2023-11-15 14:30',
status: 'pending',
vehicle: {
......@@ -204,6 +308,7 @@ const boughtOrders = ref([
},
{
id: '2',
price: 999,
date: '2023-10-28 09:15',
status: 'completed',
vehicle: {
......@@ -213,10 +318,17 @@ const boughtOrders = ref([
batteryCapacity: '电池容量:1.2kWh',
price: 2599,
imageUrl: 'https://images.unsplash.com/photo-1558981403-c5f9899a28bc?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60'
},
// 已评价信息
review: {
rating: 4,
content: '车子质量不错,续航也很给力,卖家服务态度很好,推荐购买!',
date: '2023-10-30 10:20'
}
},
{
id: '3',
price: 999,
date: '2023-09-05 16:45',
status: 'cancelled',
vehicle: {
......@@ -233,6 +345,7 @@ const boughtOrders = ref([
const soldOrders = ref([
{
id: '4',
price: 999,
date: '2023-11-10 11:20',
status: 'pending',
vehicle: {
......@@ -246,6 +359,7 @@ const soldOrders = ref([
},
{
id: '5',
price: 999,
date: '2023-10-15 13:45',
status: 'completed',
vehicle: {
......@@ -255,6 +369,12 @@ const soldOrders = ref([
batteryCapacity: '电池容量:1.6kWh',
price: 3599,
imageUrl: 'https://images.unsplash.com/photo-1558980664-3a031cf67ea8?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60'
},
// 已评价信息
review: {
rating: 5,
content: '非常满意的一次购买体验,车辆性能超出预期,卖家很专业,物流也很快!',
date: '2023-10-18 16:30'
}
}
])
......@@ -277,13 +397,6 @@ const filteredOrders = computed(() => {
})
})
// 滚动样式
const scrollStyle = computed(() => {
return {
height: 'calc(100vh - 320rpx)' // 减去头部和标签的高度
}
})
/**
* 设置视图模式(买车/卖车)
*/
......@@ -370,20 +483,42 @@ const getStatusClass = (status) => {
/**
* 处理支付
*/
const handlePayment = (orderId) => {
Taro.showToast({
title: '跳转到支付页面',
icon: 'none'
const handlePayment = ({ id, price }) => {
onPay({
id,
remain_time: 1800, // 30分钟
price
})
// TODO: 实现支付逻辑
}
/**
* 发送订单支付信息到支付组件
* @param {Object} payInfo - 支付信息
* @param {string} payInfo.id - 订单ID
* @param {number} payInfo.remain_time - 剩余时间
* @param {number} payInfo.price - 价格
*/
const onPay = ({ id, remain_time, price }) => {
show_pay.value = true
payData.value.id = id
payData.value.price = price
payData.value.remain_time = remain_time
}
/**
* 关闭支付弹框
*/
const onPayClose = () => {
show_pay.value = false
}
/**
* 查看订单详情
*/
const viewOrderDetail = (orderId) => {
Taro.navigateTo({
url: `/pages/orderDetail/index?id=${orderId}`
Taro.showToast({
title: '查看订单详情',
icon: 'none'
})
}
......@@ -391,11 +526,102 @@ const viewOrderDetail = (orderId) => {
* 评价订单
*/
const rateOrder = (orderId) => {
// 找到对应的订单
const orders = viewMode.value === 'bought' ? boughtOrders.value : soldOrders.value
const order = orders.find(o => o.id === orderId)
if (order) {
currentRateOrder.value = order
// 检查是否已有评价
if (order.review) {
// 已评价,显示只读模式
isReadOnlyMode.value = true
rateContent.value = order.review.content
rateScore.value = order.review.rating
} else {
// 未评价,显示编辑模式
isReadOnlyMode.value = false
rateContent.value = ''
rateScore.value = 5
}
showRatePopup.value = true
}
}
/**
* 关闭评价弹窗
*/
const closeRatePopup = () => {
showRatePopup.value = false
currentRateOrder.value = null
rateContent.value = ''
rateScore.value = 5
isReadOnlyMode.value = false
}
/**
* 提交评价
*/
const submitRate = async () => {
if (!rateContent.value.trim()) {
Taro.showToast({
title: '跳转到评价页面',
title: '请输入评价内容',
icon: 'none'
})
// TODO: 实现评价逻辑
return
}
if (rateScore.value < 1 || rateScore.value > 5) {
Taro.showToast({
title: '请选择评分',
icon: 'none'
})
return
}
submittingRate.value = true
try {
// 模拟提交评价
await new Promise(resolve => setTimeout(resolve, 1500))
// 更新订单评价信息
if (currentRateOrder.value) {
const currentDate = new Date()
const dateStr = `${currentDate.getFullYear()}-${String(currentDate.getMonth() + 1).padStart(2, '0')}-${String(currentDate.getDate()).padStart(2, '0')} ${String(currentDate.getHours()).padStart(2, '0')}:${String(currentDate.getMinutes()).padStart(2, '0')}`
currentRateOrder.value.review = {
rating: rateScore.value,
content: rateContent.value,
date: dateStr
}
}
Taro.showToast({
title: '评价提交成功',
icon: 'success'
})
// 关闭弹窗
closeRatePopup()
// TODO: 这里可以添加实际的API调用逻辑
// await api.submitOrderRate({
// orderId: currentRateOrder.value.id,
// rating: rateScore.value,
// content: rateContent.value
// })
} catch (error) {
Taro.showToast({
title: '提交失败,请重试',
icon: 'none'
})
} finally {
submittingRate.value = false
}
}
/**
......@@ -418,8 +644,17 @@ const deleteOrder = (orderId) => {
}
// 页面加载时的初始化
onMounted(() => {
onMounted(async () => {
// TODO: 加载订单数据
// 设置滚动列表可视高度
const windowHeight = wx.getWindowInfo().windowHeight;
setTimeout(async () => {
const headerHeight = await $('#mode-toggle').height();
const navHeight = await $('#status-tabs').height();
scrollStyle.value = {
height: windowHeight - headerHeight - navHeight + 'px'
}
}, 500);
})
</script>
......