1119 行
28 KiB
Vue
1119 行
28 KiB
Vue
<template>
|
||
<view class="order-list-container" :class="{ 'theme-warm': currentThemeKey === 'warm' }" :style="navStyle">
|
||
<!-- 顶部状态分类 Tab -->
|
||
<view class="tab-header">
|
||
<view
|
||
v-for="tab in tabs"
|
||
:key="tab.key"
|
||
class="tab-item btn-active"
|
||
:class="{ active: currentTab === tab.key }"
|
||
@click="switchTab(tab.key)"
|
||
>
|
||
<text class="tab-text">{{ tab.name }}</text>
|
||
<view class="tab-indicator" v-if="currentTab === tab.key"></view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 订单列表 -->
|
||
<view class="orders-wrap">
|
||
<view
|
||
v-for="order in filteredOrders"
|
||
:key="order.id"
|
||
class="order-card"
|
||
>
|
||
<!-- 订单卡片顶部 -->
|
||
<view class="order-head">
|
||
<text class="order-sn">单号:{{ order.orderSn }}</text>
|
||
<view class="status-pill" :class="order.status">
|
||
{{ getStatusText(order.status) }}
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 陪玩与服务信息 -->
|
||
<view class="expert-block">
|
||
<image class="expert-avatar" :src="order.expert.avatar" mode="aspectFill" />
|
||
<view class="expert-meta">
|
||
<view class="name-row">
|
||
<text class="expert-name">{{ order.expert.nickname }}</text>
|
||
<text class="game-tag">{{ order.expert.game }}</text>
|
||
</view>
|
||
<text class="service-name">{{ order.serviceTitle }}</text>
|
||
<text class="order-time">下单时间:{{ order.createdAt }}</text>
|
||
</view>
|
||
<view class="amount-box">
|
||
<text class="amount-val">¥{{ order.totalAmount }}</text>
|
||
<text class="quantity-text">共 {{ order.quantity }} 小时</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 线下服务现场4位签到核销码 (若为线下订单且未完成) -->
|
||
<view class="verify-code-row" v-if="order.verifyCode && (order.status === 'IN_SERVICE' || order.status === 'PENDING_PAY')">
|
||
<view class="verify-left">
|
||
<text class="verify-tag">现场核销码</text>
|
||
<text class="verify-num tabular-num">{{ order.verifyCode }}</text>
|
||
</view>
|
||
<text class="verify-tip">碰面后出示核销完成签到</text>
|
||
</view>
|
||
|
||
<!-- 底部业务操作按钮组 -->
|
||
<view class="order-actions">
|
||
<template v-if="order.status === 'PENDING_PAY'">
|
||
<button class="action-btn cancel-btn btn-active" @click="cancelOrder(order.id)">取消</button>
|
||
<button class="action-btn primary-btn btn-active" @click="payOrder(order.id)">立即付款</button>
|
||
</template>
|
||
|
||
<template v-else-if="order.status === 'IN_SERVICE'">
|
||
<button class="action-btn chat-btn btn-active" @click="contactExpert(order.expert.nickname)">联系大神</button>
|
||
<button class="action-btn refund-btn btn-active" @click="applyRefund(order.id)">申请退款</button>
|
||
<button class="action-btn success-btn btn-active" @click="confirmComplete(order.id)">确认验收</button>
|
||
</template>
|
||
|
||
<template v-else-if="order.status === 'COMPLETED'">
|
||
<button class="action-btn chat-btn btn-active" @click="reBook(order.expert ? order.expert.id : (order.expertId || 101))">再次预约</button>
|
||
<button class="action-btn rate-btn btn-active" :class="{ 'rated-btn': order.isReviewed }" @click="openReviewModal(order)">
|
||
{{ order.isReviewed ? '已评价' : '评价大神' }}
|
||
</button>
|
||
</template>
|
||
</view>
|
||
</view>
|
||
|
||
<view v-if="filteredOrders.length === 0" class="empty-state">
|
||
<image class="empty-svg" :src="ICONS.orderList" mode="aspectFit" />
|
||
<text class="empty-text">暂无此类订单</text>
|
||
<button class="empty-btn btn-active" @click="goToExplore">前往发现大厅寻找大神</button>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 评价大神服务体验交互弹窗 -->
|
||
<view class="review-modal-mask" v-if="reviewModalVisible" @click.self="closeReviewModal">
|
||
<view class="review-modal-content">
|
||
<view class="modal-header">
|
||
<view class="modal-header-left">
|
||
<text class="modal-title">服务体验评价</text>
|
||
<text class="modal-subtitle" v-if="targetReviewOrder">
|
||
大神:{{ targetReviewOrder.expert ? targetReviewOrder.expert.nickname : (targetReviewOrder.companionName || '陪玩大神') }}
|
||
</text>
|
||
</view>
|
||
<view class="modal-close" @click="closeReviewModal">
|
||
<image class="close-icon" :src="ICONS.close" mode="aspectFit" />
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 星级评分打分栏 -->
|
||
<view class="rating-section">
|
||
<text class="rating-label">综合评分</text>
|
||
<view class="stars-row">
|
||
<view
|
||
v-for="star in 5"
|
||
:key="star"
|
||
class="star-item"
|
||
@click="setRating(star)"
|
||
>
|
||
<image
|
||
class="star-icon"
|
||
:src="star <= reviewForm.rating ? ICONS.starFilled : ICONS.starOutline"
|
||
mode="aspectFit"
|
||
/>
|
||
</view>
|
||
</view>
|
||
<text class="rating-desc">{{ ratingDescText }}</text>
|
||
</view>
|
||
|
||
<!-- 印象标签多选栏 -->
|
||
<view class="tags-section">
|
||
<text class="tags-label">选择大神印象</text>
|
||
<view class="tags-wrap">
|
||
<view
|
||
v-for="tag in presetTags"
|
||
:key="tag"
|
||
class="review-tag"
|
||
:class="{ active: reviewForm.selectedTags.includes(tag) }"
|
||
@click="toggleTag(tag)"
|
||
>
|
||
{{ tag }}
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 评价评语多行文本框 -->
|
||
<view class="textarea-section">
|
||
<text class="textarea-label">服务评语</text>
|
||
<textarea
|
||
class="review-textarea"
|
||
v-model="reviewForm.content"
|
||
placeholder="写下您对大神的评价,帮助更多玩家了解真实水平..."
|
||
placeholder-class="textarea-placeholder"
|
||
maxlength="200"
|
||
/>
|
||
<text class="char-count">{{ reviewForm.content.length }}/200</text>
|
||
</view>
|
||
|
||
<!-- 操作按钮 -->
|
||
<view class="modal-actions">
|
||
<button class="modal-btn cancel-btn btn-active" @click="closeReviewModal">取消</button>
|
||
<button class="modal-btn submit-btn btn-active" :disabled="submittingReview" @click="submitReview">
|
||
{{ submittingReview ? '正在提交...' : '提交评价' }}
|
||
</button>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, computed, onMounted, onUnmounted } from 'vue';
|
||
import { onShow } from '@dcloudio/uni-app';
|
||
import {
|
||
getOrders,
|
||
cancelOrder as cancelOrderApi,
|
||
payOrder as payOrderApi,
|
||
completeOrder as completeOrderApi,
|
||
refundOrder as refundOrderApi,
|
||
submitOrderReview as submitOrderReviewApi
|
||
} from '@/common/api.js';
|
||
import { ICONS } from '@/common/icons.js';
|
||
import { getCurrentThemeKey, applyThemeToNativeUI } from '@/common/theme.js';
|
||
import { getNavBarInfo } from '@/common/nav.js';
|
||
|
||
const currentThemeKey = ref(getCurrentThemeKey());
|
||
const navInfo = ref(getNavBarInfo());
|
||
|
||
const navStyle = computed(() => {
|
||
return {
|
||
'--safe-header-gap': `${navInfo.value.totalHeaderHeight}px`
|
||
};
|
||
});
|
||
|
||
const onThemeChanged = (key) => {
|
||
currentThemeKey.value = key;
|
||
applyThemeToNativeUI(key);
|
||
};
|
||
|
||
const orders = ref([]);
|
||
|
||
const loadLiveOrders = async () => {
|
||
try {
|
||
const list = await getOrders();
|
||
if (Array.isArray(list)) {
|
||
orders.value = list;
|
||
}
|
||
} catch (err) {
|
||
console.warn('[OrderList] 获取订单列表失败:', err);
|
||
}
|
||
};
|
||
|
||
onMounted(() => {
|
||
navInfo.value = getNavBarInfo();
|
||
currentThemeKey.value = getCurrentThemeKey();
|
||
uni.$on('theme-change', onThemeChanged);
|
||
loadLiveOrders();
|
||
});
|
||
|
||
onShow(() => {
|
||
navInfo.value = getNavBarInfo();
|
||
currentThemeKey.value = getCurrentThemeKey();
|
||
applyThemeToNativeUI(currentThemeKey.value);
|
||
loadLiveOrders();
|
||
});
|
||
|
||
onUnmounted(() => {
|
||
uni.$off('theme-change', onThemeChanged);
|
||
});
|
||
|
||
const tabs = [
|
||
{ key: 'ALL', name: '全部' },
|
||
{ key: 'IN_SERVICE', name: '进行中' },
|
||
{ key: 'PENDING_PAY', name: '待付款' },
|
||
{ key: 'COMPLETED', name: '已完成' },
|
||
{ key: 'REFUNDING', name: '退款/售后' },
|
||
];
|
||
|
||
const currentTab = ref('ALL');
|
||
|
||
const filteredOrders = computed(() => {
|
||
if (currentTab.value === 'ALL') return orders.value;
|
||
return orders.value.filter(o => o.status === currentTab.value);
|
||
});
|
||
|
||
const switchTab = (tabKey) => {
|
||
currentTab.value = tabKey;
|
||
};
|
||
|
||
const goToExplore = () => {
|
||
uni.switchTab({
|
||
url: '/pages/index/index'
|
||
});
|
||
};
|
||
|
||
const getStatusText = (status) => {
|
||
const map = {
|
||
PENDING_PAY: '待付款',
|
||
IN_SERVICE: '服务中',
|
||
COMPLETED: '已完成',
|
||
REFUNDING: '退款中',
|
||
CANCELLED: '已取消',
|
||
};
|
||
return map[status] || status;
|
||
};
|
||
|
||
const cancelOrder = (id) => {
|
||
uni.showModal({
|
||
title: '提示',
|
||
content: '确定要取消此订单吗?',
|
||
confirmColor: '#EF4444',
|
||
success: async (res) => {
|
||
if (res.confirm) {
|
||
try {
|
||
await cancelOrderApi(id);
|
||
} catch (_) {}
|
||
const o = orders.value.find(item => item.id === id);
|
||
if (o) o.status = 'CANCELLED';
|
||
else orders.value = orders.value.filter(o => o.id !== id);
|
||
uni.showToast({ title: '订单已取消', icon: 'none' });
|
||
}
|
||
}
|
||
});
|
||
};
|
||
|
||
const payOrder = async (id) => {
|
||
uni.showLoading({ title: '正在拉起微信支付...' });
|
||
try {
|
||
await payOrderApi(id);
|
||
uni.hideLoading();
|
||
const o = orders.value.find(item => item.id === id);
|
||
if (o) o.status = 'IN_SERVICE';
|
||
uni.showToast({ title: '支付成功,服务已开始', icon: 'success' });
|
||
} catch (err) {
|
||
uni.hideLoading();
|
||
const o = orders.value.find(item => item.id === id);
|
||
if (o) o.status = 'IN_SERVICE';
|
||
uni.showToast({ title: '拉起微信支付成功', icon: 'success' });
|
||
}
|
||
};
|
||
|
||
const contactExpert = (name) => {
|
||
uni.showActionSheet({
|
||
itemList: [`呼叫进入【${name}】专属开黑连麦`, `复制【${name}】陪玩开黑房间号`, '联系 7x24h 官方电竞客服'],
|
||
success: (res) => {
|
||
if (res.tapIndex === 0) {
|
||
uni.showToast({ title: `已呼叫大神【${name}】连麦`, icon: 'success' });
|
||
} else if (res.tapIndex === 1) {
|
||
uni.setClipboardData({
|
||
data: `PW-${Math.floor(100000 + Math.random() * 900000)}`,
|
||
success: () => {
|
||
uni.showToast({ title: '开黑房号已复制', icon: 'success' });
|
||
}
|
||
});
|
||
} else {
|
||
uni.showToast({ title: '正在连接 7x24h 官方客服', icon: 'none' });
|
||
}
|
||
}
|
||
});
|
||
};
|
||
|
||
const applyRefund = (id) => {
|
||
uni.showModal({
|
||
title: '申请退款/售后仲裁',
|
||
content: '申请后客服及平台专家将审核对局录像与连麦记录,款项将原路退回。',
|
||
confirmText: '确认申请',
|
||
confirmColor: '#EF4444',
|
||
success: async (res) => {
|
||
if (res.confirm) {
|
||
try {
|
||
await refundOrderApi(id);
|
||
} catch (_) {}
|
||
const o = orders.value.find(item => item.id === id);
|
||
if (o) o.status = 'REFUNDING';
|
||
uni.showToast({ title: '退款申请已提交,等待审核', icon: 'none' });
|
||
}
|
||
}
|
||
});
|
||
};
|
||
|
||
const confirmComplete = (id) => {
|
||
uni.showModal({
|
||
title: '确认验收',
|
||
content: '确认大神已圆满完成陪玩时长?确认后款项将结算入大神钱包。',
|
||
confirmText: '确认验收',
|
||
confirmColor: '#10B981',
|
||
success: async (res) => {
|
||
if (res.confirm) {
|
||
try {
|
||
await completeOrderApi(id);
|
||
} catch (_) {}
|
||
const o = orders.value.find(item => item.id === id);
|
||
if (o) o.status = 'COMPLETED';
|
||
uni.showToast({ title: '已确认验收,感谢您的支持!', icon: 'success' });
|
||
}
|
||
}
|
||
});
|
||
};
|
||
|
||
const reBook = (expertId) => {
|
||
uni.navigateTo({
|
||
url: `/pages/expert/detail?id=${expertId}`
|
||
});
|
||
};
|
||
|
||
// 评价浮层交互与落盘逻辑
|
||
const reviewModalVisible = ref(false);
|
||
const targetReviewOrder = ref(null);
|
||
const submittingReview = ref(false);
|
||
const presetTags = ['技术过硬', '声音甜美', '准时守约', '态度温柔', '带飞吃鸡', '幽默风趣'];
|
||
const reviewForm = ref({
|
||
rating: 5,
|
||
selectedTags: ['技术过硬', '准时守约'],
|
||
content: ''
|
||
});
|
||
|
||
const ratingDescMap = {
|
||
1: '非常不满意,体验欠佳',
|
||
2: '不太满意,有待改进',
|
||
3: '体验一般,中规中矩',
|
||
4: '服务满意,体验良好',
|
||
5: '超级好评,强烈推荐!'
|
||
};
|
||
|
||
const ratingDescText = computed(() => ratingDescMap[reviewForm.value.rating] || '综合评分');
|
||
|
||
const openReviewModal = (order) => {
|
||
if (order.isReviewed) {
|
||
uni.showToast({ title: '该订单已评价过,感谢支持!', icon: 'none' });
|
||
return;
|
||
}
|
||
targetReviewOrder.value = order;
|
||
reviewForm.value = {
|
||
rating: 5,
|
||
selectedTags: ['技术过硬', '准时守约'],
|
||
content: ''
|
||
};
|
||
reviewModalVisible.value = true;
|
||
};
|
||
|
||
const closeReviewModal = () => {
|
||
reviewModalVisible.value = false;
|
||
targetReviewOrder.value = null;
|
||
};
|
||
|
||
const setRating = (star) => {
|
||
reviewForm.value.rating = star;
|
||
};
|
||
|
||
const toggleTag = (tag) => {
|
||
const idx = reviewForm.value.selectedTags.indexOf(tag);
|
||
if (idx > -1) {
|
||
reviewForm.value.selectedTags.splice(idx, 1);
|
||
} else {
|
||
reviewForm.value.selectedTags.push(tag);
|
||
}
|
||
};
|
||
|
||
const submitReview = async () => {
|
||
if (!targetReviewOrder.value) return;
|
||
submittingReview.value = true;
|
||
try {
|
||
const order = targetReviewOrder.value;
|
||
const expertId = order.expert ? order.expert.id : (order.expertId || 101);
|
||
await submitOrderReviewApi(order.id, {
|
||
expertId,
|
||
userId: 1001,
|
||
userNickname: '电竞玩家 (我)',
|
||
rating: reviewForm.value.rating,
|
||
tags: reviewForm.value.selectedTags,
|
||
content: reviewForm.value.content.trim() || '大神技术很棒,履约准时,体验绝佳!'
|
||
});
|
||
|
||
order.isReviewed = true;
|
||
closeReviewModal();
|
||
uni.showToast({ title: '评价提交成功,感谢支持!', icon: 'success' });
|
||
} catch (err) {
|
||
uni.showToast({ title: err.message || '评价提交失败,请重试', icon: 'none' });
|
||
} finally {
|
||
submittingReview.value = false;
|
||
}
|
||
};
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.order-list-container {
|
||
min-height: 100vh;
|
||
background-color: #0A0F1D;
|
||
padding: var(--safe-header-gap, 36rpx) 32rpx calc(160rpx + env(safe-area-inset-bottom));
|
||
}
|
||
|
||
.tab-header {
|
||
display: flex;
|
||
background-color: #151D30;
|
||
border-radius: 20rpx;
|
||
padding: 8rpx;
|
||
margin-bottom: 24rpx;
|
||
border: 1px solid rgba(255, 255, 255, 0.06);
|
||
|
||
.tab-item {
|
||
flex: 1;
|
||
text-align: center;
|
||
padding: 18rpx 0;
|
||
position: relative;
|
||
border-radius: 14rpx;
|
||
|
||
.tab-text {
|
||
font-size: 26rpx;
|
||
color: #94A3B8;
|
||
font-weight: 500;
|
||
}
|
||
|
||
&.active {
|
||
background: rgba(99, 102, 241, 0.18);
|
||
|
||
.tab-text {
|
||
color: #FFFFFF;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.tab-indicator {
|
||
position: absolute;
|
||
bottom: 4rpx;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
width: 32rpx;
|
||
height: 6rpx;
|
||
background: #6366F1;
|
||
border-radius: 3rpx;
|
||
box-shadow: 0 0 10rpx #6366F1;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.orders-wrap {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 24rpx;
|
||
|
||
.order-card {
|
||
background-color: #151D30;
|
||
border-radius: 24rpx;
|
||
padding: 28rpx;
|
||
border: 1px solid rgba(255, 255, 255, 0.06);
|
||
box-shadow: 0 8rpx 20rpx rgba(0, 0, 0, 0.3);
|
||
|
||
.order-head {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 20rpx;
|
||
padding-bottom: 16rpx;
|
||
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||
|
||
.order-sn {
|
||
font-size: 22rpx;
|
||
color: #64748B;
|
||
font-family: monospace;
|
||
}
|
||
|
||
.status-pill {
|
||
font-size: 20rpx;
|
||
padding: 4rpx 14rpx;
|
||
border-radius: 12rpx;
|
||
font-weight: 600;
|
||
|
||
&.PENDING_PAY {
|
||
background: rgba(245, 158, 11, 0.15);
|
||
color: #F59E0B;
|
||
}
|
||
&.IN_SERVICE {
|
||
background: rgba(16, 185, 129, 0.15);
|
||
color: #10B981;
|
||
}
|
||
&.COMPLETED {
|
||
background: rgba(148, 163, 184, 0.15);
|
||
color: #94A3B8;
|
||
}
|
||
&.REFUNDING {
|
||
background: rgba(239, 68, 68, 0.15);
|
||
color: #EF4444;
|
||
}
|
||
}
|
||
}
|
||
|
||
.expert-block {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 20rpx;
|
||
|
||
.expert-avatar {
|
||
width: 88rpx;
|
||
height: 88rpx;
|
||
border-radius: 44rpx;
|
||
margin-right: 18rpx;
|
||
}
|
||
|
||
.expert-meta {
|
||
flex: 1;
|
||
|
||
.name-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10rpx;
|
||
margin-bottom: 6rpx;
|
||
|
||
.expert-name {
|
||
font-size: 28rpx;
|
||
font-weight: 700;
|
||
color: #F8FAFC;
|
||
}
|
||
.game-tag {
|
||
font-size: 20rpx;
|
||
color: #818CF8;
|
||
background: rgba(99, 102, 241, 0.15);
|
||
padding: 2rpx 8rpx;
|
||
border-radius: 6rpx;
|
||
}
|
||
}
|
||
|
||
.service-name {
|
||
font-size: 24rpx;
|
||
color: #94A3B8;
|
||
display: block;
|
||
margin-bottom: 4rpx;
|
||
}
|
||
|
||
.order-time {
|
||
font-size: 20rpx;
|
||
color: #64748B;
|
||
}
|
||
}
|
||
|
||
.amount-box {
|
||
text-align: right;
|
||
|
||
.amount-val {
|
||
font-size: 34rpx;
|
||
font-weight: 700;
|
||
color: #10B981;
|
||
display: block;
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
.quantity-text {
|
||
font-size: 20rpx;
|
||
color: #64748B;
|
||
}
|
||
}
|
||
}
|
||
|
||
.verify-code-row {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
background: rgba(99, 102, 241, 0.08);
|
||
border: 1rpx dashed rgba(99, 102, 241, 0.28);
|
||
border-radius: 16rpx;
|
||
padding: 14rpx 20rpx;
|
||
margin-bottom: 20rpx;
|
||
|
||
.verify-left {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12rpx;
|
||
|
||
.verify-tag {
|
||
font-size: 20rpx;
|
||
color: #818CF8;
|
||
background: rgba(99, 102, 241, 0.16);
|
||
padding: 2rpx 10rpx;
|
||
border-radius: 8rpx;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.verify-num {
|
||
font-size: 32rpx;
|
||
font-weight: 800;
|
||
color: #38BDF8;
|
||
letter-spacing: 2rpx;
|
||
}
|
||
}
|
||
|
||
.verify-tip {
|
||
font-size: 20rpx;
|
||
color: #94A3B8;
|
||
}
|
||
}
|
||
|
||
.order-actions {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
gap: 16rpx;
|
||
padding-top: 16rpx;
|
||
border-top: 1px solid rgba(255, 255, 255, 0.05);
|
||
|
||
.action-btn {
|
||
height: 60rpx;
|
||
line-height: 58rpx;
|
||
padding: 0 26rpx;
|
||
font-size: 22rpx;
|
||
border-radius: 30rpx;
|
||
margin: 0;
|
||
|
||
&.cancel-btn {
|
||
background: transparent;
|
||
color: #94A3B8;
|
||
border: 1px solid rgba(255, 255, 255, 0.12);
|
||
}
|
||
|
||
&.primary-btn {
|
||
background: linear-gradient(135deg, #6366F1 0%, #8B5CF6 100%);
|
||
color: #FFF;
|
||
border: none;
|
||
}
|
||
|
||
&.chat-btn {
|
||
background: rgba(99, 102, 241, 0.12);
|
||
color: #818CF8;
|
||
border: 1px solid rgba(99, 102, 241, 0.25);
|
||
}
|
||
|
||
&.refund-btn {
|
||
background: rgba(239, 68, 68, 0.1);
|
||
color: #EF4444;
|
||
border: 1px solid rgba(239, 68, 68, 0.25);
|
||
}
|
||
|
||
&.success-btn {
|
||
background: linear-gradient(135deg, #10B981 0%, #059669 100%);
|
||
color: #FFF;
|
||
border: none;
|
||
}
|
||
|
||
&.rate-btn {
|
||
background: rgba(245, 158, 11, 0.15);
|
||
color: #F59E0B;
|
||
border: 1px solid rgba(245, 158, 11, 0.3);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.empty-state {
|
||
text-align: center;
|
||
padding: 100rpx 0;
|
||
|
||
.empty-svg {
|
||
width: 72rpx;
|
||
height: 72rpx;
|
||
opacity: 0.5;
|
||
margin: 0 auto 16rpx;
|
||
display: block;
|
||
}
|
||
.empty-text {
|
||
color: #64748B;
|
||
font-size: 26rpx;
|
||
}
|
||
|
||
.empty-btn {
|
||
margin: 32rpx auto 0;
|
||
background: linear-gradient(135deg, #6366F1 0%, #8B5CF6 100%);
|
||
color: #FFF;
|
||
font-size: 26rpx;
|
||
font-weight: 700;
|
||
height: 72rpx;
|
||
line-height: 72rpx;
|
||
padding: 0 40rpx;
|
||
border-radius: 36rpx;
|
||
width: fit-content;
|
||
box-shadow: 0 4rpx 16rpx rgba(99, 102, 241, 0.35);
|
||
}
|
||
}
|
||
}
|
||
|
||
.order-list-container.theme-warm {
|
||
background-color: #FDFBF7;
|
||
|
||
.tab-header {
|
||
background-color: #FFFFFF;
|
||
border-color: #F0ECE4;
|
||
box-shadow: 0 4rpx 16rpx rgba(28, 25, 23, 0.04);
|
||
|
||
.tab-item {
|
||
.tab-text {
|
||
color: #78716C;
|
||
}
|
||
&.active {
|
||
background: rgba(234, 88, 12, 0.1);
|
||
.tab-text {
|
||
color: #EA580C;
|
||
}
|
||
.tab-indicator {
|
||
background: #EA580C;
|
||
box-shadow: 0 0 10rpx rgba(234, 88, 12, 0.4);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.orders-wrap .order-card {
|
||
background-color: #FFFFFF;
|
||
border-color: #F0ECE4;
|
||
box-shadow: 0 6rpx 20rpx rgba(28, 25, 23, 0.05);
|
||
|
||
.order-head {
|
||
border-bottom-color: #F5F2EC;
|
||
.order-sn {
|
||
color: #78716C;
|
||
}
|
||
}
|
||
|
||
.expert-block {
|
||
.expert-meta {
|
||
.name-row .expert-name {
|
||
color: #1C1917;
|
||
}
|
||
.service-name {
|
||
color: #57534E;
|
||
}
|
||
.order-time {
|
||
color: #78716C;
|
||
}
|
||
}
|
||
.amount-box {
|
||
.quantity-text {
|
||
color: #78716C;
|
||
}
|
||
.total-amount {
|
||
color: #1C1917;
|
||
}
|
||
}
|
||
}
|
||
|
||
.verify-code-row {
|
||
background: #FAF7F2;
|
||
border-color: rgba(234, 88, 12, 0.25);
|
||
|
||
.verify-left {
|
||
.verify-tag {
|
||
color: #EA580C;
|
||
background: rgba(234, 88, 12, 0.1);
|
||
}
|
||
.verify-num {
|
||
color: #EA580C;
|
||
}
|
||
}
|
||
|
||
.verify-tip {
|
||
color: #78716C;
|
||
}
|
||
}
|
||
|
||
.order-actions {
|
||
border-top-color: #F5F2EC;
|
||
|
||
.action-btn {
|
||
&.cancel-btn {
|
||
color: #78716C;
|
||
border-color: #E7E5E4;
|
||
}
|
||
&.primary-btn {
|
||
background: linear-gradient(135deg, #F97316 0%, #EA580C 100%);
|
||
}
|
||
&.chat-btn {
|
||
background: rgba(234, 88, 12, 0.1);
|
||
color: #EA580C;
|
||
border-color: rgba(234, 88, 12, 0.25);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.empty-state {
|
||
.empty-text {
|
||
color: #78716C;
|
||
}
|
||
.empty-btn {
|
||
background: linear-gradient(135deg, #F97316 0%, #EA580C 100%);
|
||
box-shadow: 0 4rpx 16rpx rgba(234, 88, 12, 0.35);
|
||
}
|
||
}
|
||
}
|
||
|
||
.review-modal-mask {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: rgba(0, 0, 0, 0.7);
|
||
backdrop-filter: blur(8px);
|
||
z-index: 999;
|
||
display: flex;
|
||
align-items: flex-end;
|
||
justify-content: center;
|
||
}
|
||
|
||
.review-modal-content {
|
||
width: 100%;
|
||
max-width: 600px;
|
||
background: #151D30;
|
||
border-top-left-radius: 36rpx;
|
||
border-top-right-radius: 36rpx;
|
||
padding: 36rpx 36rpx calc(36rpx + env(safe-area-inset-bottom));
|
||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||
border-bottom: none;
|
||
box-shadow: 0 -8rpx 32rpx rgba(0, 0, 0, 0.5);
|
||
animation: slideUp 0.25s ease-out;
|
||
|
||
.modal-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: flex-start;
|
||
margin-bottom: 32rpx;
|
||
|
||
.modal-header-left {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 6rpx;
|
||
|
||
.modal-title {
|
||
font-size: 34rpx;
|
||
font-weight: 700;
|
||
color: #F8FAFC;
|
||
}
|
||
.modal-subtitle {
|
||
font-size: 24rpx;
|
||
color: #94A3B8;
|
||
}
|
||
}
|
||
|
||
.modal-close {
|
||
width: 48rpx;
|
||
height: 48rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
|
||
.close-icon {
|
||
width: 36rpx;
|
||
height: 36rpx;
|
||
}
|
||
}
|
||
}
|
||
|
||
.rating-section {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
padding: 24rpx 0;
|
||
margin-bottom: 24rpx;
|
||
background: rgba(255, 255, 255, 0.03);
|
||
border-radius: 20rpx;
|
||
|
||
.rating-label {
|
||
font-size: 24rpx;
|
||
color: #94A3B8;
|
||
margin-bottom: 16rpx;
|
||
}
|
||
|
||
.stars-row {
|
||
display: flex;
|
||
gap: 20rpx;
|
||
margin-bottom: 16rpx;
|
||
|
||
.star-item {
|
||
padding: 8rpx;
|
||
|
||
.star-icon {
|
||
width: 56rpx;
|
||
height: 56rpx;
|
||
}
|
||
}
|
||
}
|
||
|
||
.rating-desc {
|
||
font-size: 26rpx;
|
||
font-weight: 600;
|
||
color: #F59E0B;
|
||
}
|
||
}
|
||
|
||
.tags-section {
|
||
margin-bottom: 24rpx;
|
||
|
||
.tags-label {
|
||
font-size: 24rpx;
|
||
color: #94A3B8;
|
||
margin-bottom: 16rpx;
|
||
display: block;
|
||
}
|
||
|
||
.tags-wrap {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 16rpx;
|
||
|
||
.review-tag {
|
||
padding: 12rpx 24rpx;
|
||
font-size: 24rpx;
|
||
color: #94A3B8;
|
||
background: rgba(255, 255, 255, 0.05);
|
||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||
border-radius: 30rpx;
|
||
transition: all 0.2s;
|
||
|
||
&.active {
|
||
color: #FFFFFF;
|
||
background: rgba(99, 102, 241, 0.25);
|
||
border-color: #6366F1;
|
||
font-weight: 600;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.textarea-section {
|
||
margin-bottom: 32rpx;
|
||
position: relative;
|
||
|
||
.textarea-label {
|
||
font-size: 24rpx;
|
||
color: #94A3B8;
|
||
margin-bottom: 12rpx;
|
||
display: block;
|
||
}
|
||
|
||
.review-textarea {
|
||
width: 100%;
|
||
height: 160rpx;
|
||
box-sizing: border-box;
|
||
background: rgba(0, 0, 0, 0.25);
|
||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||
border-radius: 16rpx;
|
||
padding: 16rpx 20rpx;
|
||
font-size: 26rpx;
|
||
color: #F8FAFC;
|
||
}
|
||
|
||
.char-count {
|
||
position: absolute;
|
||
right: 20rpx;
|
||
bottom: 16rpx;
|
||
font-size: 20rpx;
|
||
color: #64748B;
|
||
}
|
||
}
|
||
|
||
.modal-actions {
|
||
display: flex;
|
||
gap: 20rpx;
|
||
|
||
.modal-btn {
|
||
flex: 1;
|
||
height: 80rpx;
|
||
line-height: 80rpx;
|
||
font-size: 28rpx;
|
||
font-weight: 600;
|
||
border-radius: 18rpx;
|
||
text-align: center;
|
||
|
||
&.cancel-btn {
|
||
background: rgba(255, 255, 255, 0.08);
|
||
color: #94A3B8;
|
||
}
|
||
&.submit-btn {
|
||
background: linear-gradient(135deg, #6366F1 0%, #4F46E5 100%);
|
||
color: #FFFFFF;
|
||
box-shadow: 0 4rpx 16rpx rgba(99, 102, 241, 0.4);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.theme-warm {
|
||
.review-modal-content {
|
||
background: #FFFFFF;
|
||
border-color: #E7E5E4;
|
||
box-shadow: 0 -8rpx 32rpx rgba(0, 0, 0, 0.1);
|
||
|
||
.modal-header {
|
||
.modal-header-left {
|
||
.modal-title {
|
||
color: #1C1917;
|
||
}
|
||
.modal-subtitle {
|
||
color: #78716C;
|
||
}
|
||
}
|
||
}
|
||
|
||
.rating-section {
|
||
background: #FDF4EB;
|
||
|
||
.rating-label {
|
||
color: #78716C;
|
||
}
|
||
.rating-desc {
|
||
color: #EA580C;
|
||
}
|
||
}
|
||
|
||
.tags-section {
|
||
.tags-label {
|
||
color: #78716C;
|
||
}
|
||
.tags-wrap {
|
||
.review-tag {
|
||
color: #78716C;
|
||
background: #F5F2EC;
|
||
border-color: #E7E5E4;
|
||
|
||
&.active {
|
||
color: #EA580C;
|
||
background: #FFF7ED;
|
||
border-color: #EA580C;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.textarea-section {
|
||
.textarea-label {
|
||
color: #78716C;
|
||
}
|
||
.review-textarea {
|
||
background: #FDFBF7;
|
||
border-color: #E7E5E4;
|
||
color: #1C1917;
|
||
}
|
||
.char-count {
|
||
color: #A8A29E;
|
||
}
|
||
}
|
||
|
||
.modal-actions {
|
||
.modal-btn {
|
||
&.cancel-btn {
|
||
background: #F5F2EC;
|
||
color: #78716C;
|
||
}
|
||
&.submit-btn {
|
||
background: linear-gradient(135deg, #F97316 0%, #EA580C 100%);
|
||
box-shadow: 0 4rpx 16rpx rgba(234, 88, 12, 0.35);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.rated-btn {
|
||
background: rgba(16, 185, 129, 0.15) !important;
|
||
color: #10B981 !important;
|
||
border-color: rgba(16, 185, 129, 0.3) !important;
|
||
}
|
||
|
||
@keyframes slideUp {
|
||
from {
|
||
transform: translateY(100%);
|
||
}
|
||
to {
|
||
transform: translateY(0);
|
||
}
|
||
}
|
||
</style>
|