project:强制覆盖代码,更新.gitignore文件内容

This commit is contained in:
2025-09-16 17:23:51 +08:00
parent ab9a7279f5
commit 5ad77a857c
97 changed files with 2295 additions and 1751 deletions
+7 -6
View File
@@ -125,7 +125,7 @@
</view>
<!-- <view class="" style="align-items: center;align-content: center;text-align: center;line-height: 50rpx;"
@click="handleRent('wx-pay')">
无法免押点这里></view> -->
无法免押点这里</view> -->
</view>
<!-- 手机号授权弹窗 -->
@@ -195,7 +195,7 @@
const showPhoneAuthPopup = ref(false)
// 生命周期 onLoad 钩子
onLoad((options) => {
onLoad(async(options) => {
if (options.deviceNo != uni.getStorageSync('deviceId') || !uni.getStorageSync('deviceId')) {
deviceId.value = options.deviceNo
uni.setStorageSync('deviceId', options.deviceNo)
@@ -203,16 +203,17 @@
deviceId.value = uni.getStorageSync('deviceId')
// uni.removeStorageSync('deviceId')
}
checkOrderStatus()
await checkOrderStatus()
await fetchDeviceInfo()
})
onMounted(async () => {
await checkUserPhone()
})
onShow(async () => {
await fetchDeviceInfo()
})
// onShow(async () => {
// await fetchDeviceInfo()
// })
const checkUserPhone = async () => {
try {
+267
View File
@@ -0,0 +1,267 @@
<template>
<view class="express-form-container">
<!-- 页头订单摘要商务风卡片 -->
<view class="card order-summary" v-if="orderInfo.orderNo">
<view class="summary-row">
<view class="label">订单号</view>
<view class="value">{{ orderInfo.orderNo }}</view>
</view>
<view class="summary-row" v-if="orderInfo.deviceNo">
<view class="label">设备号</view>
<view class="value">{{ orderInfo.deviceNo }}</view>
</view>
<view class="summary-row" v-if="orderInfo.startTime">
<view class="label">开始时间</view>
<view class="value">{{ orderInfo.startTime }}</view>
</view>
</view>
<!-- 表单卡片 -->
<view class="card form-card">
<view class="form-title">填写快递归还信息</view>
<view class="form-item">
<view class="item-label">联系电话</view>
<input class="item-input" type="number" v-model="phone" placeholder="请输入联系电话" maxlength="20" />
</view>
<view class="form-item">
<view class="item-label">快递单号</view>
<input class="item-input" type="text" v-model="trackingNumber" placeholder="请输入快递单号" maxlength="40" />
</view>
<view class="tips" v-if="tipsText">{{ tipsText }}</view>
</view>
<!-- 提交操作条 -->
<view class="bottom-bar">
<view class="action-item primary" @click="handleSubmit">提交信息</view>
</view>
</view>
</template>
<script setup>
import {
ref,
reactive
} from 'vue'
import {
onLoad
} from '@dcloudio/uni-app'
import {
queryById
} from '@/config/user.js'
const orderId = ref('')
const orderInfo = reactive({
orderNo: '',
deviceNo: '',
startTime: ''
})
const phone = ref('')
const trackingNumber = ref('')
const tipsText = ref('')
onLoad(async (options) => {
orderId.value = options?.orderId || ''
if (!orderId.value) {
uni.showToast({
title: '缺少订单号',
icon: 'none'
})
setTimeout(() => {
uni.navigateBack()
}, 1200)
return
}
await loadOrder()
})
const loadOrder = async () => {
try {
uni.showLoading({
title: '加载中'
})
const res = await queryById(orderId.value)
if (res?.code === 200 && res.data) {
orderInfo.orderNo = res.data.orderNo || res.data.orderId || ''
orderInfo.deviceNo = res.data.deviceNo || ''
orderInfo.startTime = res.data.startTime || res.data.createTime || ''
// 默认联系电话可回填订单上的手机号(若有)
if (res.data.phone && !phone.value) phone.value = res.data.phone
} else {
throw new Error(res?.msg || '获取订单失败')
}
} catch (e) {
uni.showToast({
title: e.message || '加载失败',
icon: 'none'
})
} finally {
uni.hideLoading()
}
}
const validate = () => {
// 简单手机检查(兼容座机/国际号,放宽到至少 5 位数字)
const digits = (phone.value || '').replace(/\D/g, '')
if (!digits || digits.length < 5) {
uni.showToast({
title: '请填写有效联系电话',
icon: 'none'
})
return false
}
if (!trackingNumber.value) {
uni.showToast({
title: '请填写快递单号',
icon: 'none'
})
return false
}
return true
}
const handleSubmit = async () => {
if (!validate()) return
// 这里保留与后端联调位置(未给出具体提交接口,先本地成功提示)
try {
uni.showLoading({
title: '提交中'
})
// 示例:
// await uni.request({
// url: `${URL}/app/express-return/submit`,
// method: 'POST',
// header: { 'Authorization': `Bearer ${uni.getStorageSync('token')}` },
// data: { orderId: orderId.value, phone: phone.value, trackingNumber: trackingNumber.value }
// })
await new Promise(r => setTimeout(r, 500))
uni.showToast({
title: '提交成功',
icon: 'success'
})
setTimeout(() => {
uni.navigateBack()
}, 800)
} catch (e) {
uni.showToast({
title: e.message || '提交失败',
icon: 'none'
})
} finally {
uni.hideLoading()
}
}
</script>
<style lang="scss" scoped>
.express-form-container {
min-height: 100vh;
background: #f7f8fa;
padding: 30rpx 30rpx 200rpx;
}
.card {
background: #ffffff;
border-radius: 20rpx;
padding: 30rpx;
margin-bottom: 24rpx;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
}
.order-summary {
.summary-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 14rpx 0;
border-bottom: 1rpx solid #f0f2f5;
&:last-child {
border-bottom: 0;
}
.label {
font-size: 26rpx;
color: #6b7280;
}
.value {
font-size: 28rpx;
color: #111827;
}
}
}
.form-card {
.form-title {
font-size: 30rpx;
font-weight: 600;
color: #111827;
margin-bottom: 16rpx;
}
.form-item {
padding: 22rpx 0;
border-bottom: 1rpx solid #f0f2f5;
&:last-child {
border-bottom: 0;
}
.item-label {
font-size: 28rpx;
color: #374151;
margin-bottom: 10rpx;
}
.item-input {
display: block;
width: 100%;
height: 50rpx;
box-sizing: border-box;
background: #ffffff;
border: 1rpx solid #e5e7eb;
border-radius: 12rpx;
padding: 0rpx 14rpx;
font-size: 28rpx;
color: #111827;
line-height: 1.2;
}
}
.tips {
margin-top: 12rpx;
font-size: 24rpx;
color: #6b7280;
}
}
.bottom-bar {
position: fixed;
left: 0;
right: 0;
bottom: 0;
padding: 24rpx 30rpx;
padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
background: #ffffff;
box-shadow: 0 -4rpx 16rpx rgba(0, 0, 0, 0.06);
display: block;
.action-item {
width: 100%;
height: 92rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 44rpx;
font-size: 30rpx;
font-weight: 600;
&.primary {
background: #1f2937;
color: #fff;
}
}
}
</style>
+366
View File
@@ -0,0 +1,366 @@
<template>
<view class="express-detail-container">
<!-- 状态卡片 -->
<view class="status-card">
<view class="status-header">
<view class="status-icon" :class="getStatusClass(detailData.status)">
<text class="icon-text">{{ getStatusIcon(detailData.status) }}</text>
</view>
<view class="status-info">
<text class="status-title">{{ getStatusText(detailData.status) }}</text>
<text class="status-desc">{{ getStatusDesc(detailData.status) }}</text>
</view>
</view>
</view>
<!-- 快递信息卡片 -->
<view class="info-card">
<view class="card-title">
<text class="title-text">快递信息</text>
</view>
<view class="info-list">
<view class="info-item">
<text class="label">快递公司</text>
<text class="value">{{ detailData.expressCompany }}</text>
</view>
<view class="info-item">
<text class="label">运单号</text>
<text class="value tracking-number">{{ detailData.trackingNumber }}</text>
</view>
<view class="info-item">
<text class="label">包裹类型</text>
<text class="value">{{ detailData.packageType }}</text>
</view>
<view class="info-item">
<text class="label">包裹重量</text>
<text class="value">{{ detailData.weight }}</text>
</view>
</view>
</view>
<!-- 归还信息卡片 -->
<view class="info-card">
<view class="card-title">
<text class="title-text">归还信息</text>
</view>
<view class="info-list">
<view class="info-item">
<text class="label">归还地址</text>
<text class="value address">{{ detailData.returnAddress }}</text>
</view>
<view class="info-item">
<text class="label">归还时间</text>
<text class="value">{{ detailData.returnTime }}</text>
</view>
<view class="info-item">
<text class="label">处理时间</text>
<text class="value">{{ detailData.processTime || '--' }}</text>
</view>
<view class="info-item">
<text class="label">完成时间</text>
<text class="value">{{ detailData.completeTime || '--' }}</text>
</view>
</view>
</view>
<!-- 备注信息卡片 -->
<view class="info-card" v-if="detailData.remark">
<view class="card-title">
<text class="title-text">备注信息</text>
</view>
<view class="remark-content">
<text class="remark-text">{{ detailData.remark }}</text>
</view>
</view>
<!-- 操作按钮 -->
<view class="action-buttons">
<button class="action-btn primary" @click="handleCopyTracking">
<text class="btn-text">复制运单号</text>
</button>
<button class="action-btn secondary" @click="handleContactService">
<text class="btn-text">联系客服</text>
</button>
</view>
</view>
</template>
<script setup>
import { ref, onMounted } from 'vue'
// 详情数据
const detailData = ref({
id: 1,
expressCompany: '顺丰速运',
trackingNumber: 'SF1234567890123',
returnAddress: '北京市朝阳区建国门外大街1号',
returnTime: '2024-01-15 14:30:00',
processTime: '2024-01-15 15:00:00',
completeTime: '2024-01-15 16:30:00',
packageType: '文件',
weight: '0.5kg',
status: 'completed',
remark: '包裹已安全送达,感谢您的使用!'
})
// 获取状态样式类
const getStatusClass = (status) => {
const statusMap = {
'completed': 'status-completed',
'processing': 'status-processing',
'pending': 'status-pending'
}
return statusMap[status] || 'status-pending'
}
// 获取状态图标
const getStatusIcon = (status) => {
const iconMap = {
'completed': '✓',
'processing': '⏳',
'pending': '⏸'
}
return iconMap[status] || '⏸'
}
// 获取状态文本
const getStatusText = (status) => {
const textMap = {
'completed': '归还完成',
'processing': '处理中',
'pending': '待处理'
}
return textMap[status] || '待处理'
}
// 获取状态描述
const getStatusDesc = (status) => {
const descMap = {
'completed': '您的快递已成功归还',
'processing': '正在处理您的归还请求',
'pending': '等待处理归还申请'
}
return descMap[status] || '等待处理归还申请'
}
// 复制运单号
const handleCopyTracking = () => {
uni.setClipboardData({
data: detailData.value.trackingNumber,
success: () => {
uni.showToast({
title: '运单号已复制',
icon: 'success'
})
}
})
}
// 联系客服
const handleContactService = () => {
uni.showModal({
title: '联系客服',
content: '客服电话:400-123-4567\n工作时间:9:00-18:00',
confirmText: '拨打',
cancelText: '取消',
success: (res) => {
if (res.confirm) {
uni.makePhoneCall({
phoneNumber: '400-123-4567'
})
}
}
})
}
// 页面加载时获取详情数据
onMounted(() => {
// 这里可以根据路由参数获取详情数据
const pages = getCurrentPages()
const currentPage = pages[pages.length - 1]
const options = currentPage.options
if (options.id) {
// 根据ID获取详情数据
console.log('获取详情数据,ID:', options.id)
// 这里可以调用API获取详情数据
}
})
</script>
<style lang="scss">
.express-detail-container {
min-height: 100vh;
background-color: #f8f9fa;
padding: 20rpx;
}
.status-card {
background-color: #ffffff;
border-radius: 16rpx;
padding: 40rpx 24rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
}
.status-header {
display: flex;
align-items: center;
}
.status-icon {
width: 100rpx;
height: 100rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 40rpx;
font-weight: bold;
margin-right: 24rpx;
&.status-completed {
background-color: #e8f5e8;
color: #27ae60;
}
&.status-processing {
background-color: #fff3cd;
color: #f39c12;
}
&.status-pending {
background-color: #f8f9fa;
color: #6c757d;
}
}
.status-info {
flex: 1;
}
.status-title {
display: block;
font-size: 36rpx;
font-weight: 600;
color: #2c3e50;
margin-bottom: 8rpx;
}
.status-desc {
display: block;
font-size: 28rpx;
color: #7f8c8d;
}
.info-card {
background-color: #ffffff;
border-radius: 16rpx;
padding: 24rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
}
.card-title {
margin-bottom: 24rpx;
padding-bottom: 16rpx;
border-bottom: 1rpx solid #f1f3f4;
}
.title-text {
font-size: 32rpx;
font-weight: 600;
color: #2c3e50;
}
.info-list {
.info-item {
display: flex;
justify-content: space-between;
align-items: flex-start;
padding: 16rpx 0;
border-bottom: 1rpx solid #f8f9fa;
&:last-child {
border-bottom: none;
}
}
}
.label {
font-size: 28rpx;
color: #7f8c8d;
min-width: 160rpx;
}
.value {
font-size: 28rpx;
color: #2c3e50;
flex: 1;
text-align: right;
&.tracking-number {
font-family: 'Courier New', monospace;
font-weight: 500;
}
&.address {
text-align: right;
line-height: 1.4;
}
}
.remark-content {
padding: 16rpx 0;
}
.remark-text {
font-size: 28rpx;
color: #34495e;
line-height: 1.5;
}
.action-buttons {
display: flex;
gap: 20rpx;
margin-top: 40rpx;
padding: 0 20rpx;
}
.action-btn {
flex: 1;
height: 88rpx;
border-radius: 44rpx;
border: none;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
font-weight: 500;
transition: all 0.2s ease;
&.primary {
background-color: #1976D2;
color: #ffffff;
&:active {
background-color: #1565C0;
}
}
&.secondary {
background-color: #ffffff;
color: #1976D2;
border: 2rpx solid #1976D2;
&:active {
background-color: #f8f9fa;
}
}
}
.btn-text {
font-size: 32rpx;
font-weight: 500;
}
</style>
+289
View File
@@ -0,0 +1,289 @@
<template>
<view class="express-return-container">
<!-- 页面标题 -->
<!-- <view class="page-header">
<text class="page-title">快递归还</text>
<text class="page-subtitle">查看您的快递归还记录</text>
</view> -->
<!-- 列表容器 -->
<view class="list-container">
<view class="return-item" v-for="(item, index) in returnList" :key="index" @click="handleItemClick(item)">
<!-- 左侧图标 -->
<view class="item-icon">
<view class="icon-wrapper" :class="getStatusClass(item.status)">
<text class="icon-text">{{ getStatusIcon(item.status) }}</text>
</view>
</view>
<!-- 中间内容 -->
<view class="item-content">
<view class="item-header">
<text class="express-company">{{ item.expressCompany }}</text>
<text class="tracking-number">运单号{{ item.trackingNumber }}</text>
</view>
<view class="item-details">
<text class="return-address">归还地址{{ item.returnAddress }}</text>
<text class="return-time">归还时间{{ item.returnTime }}</text>
</view>
<view class="item-footer">
<text class="package-info">包裹{{ item.packageType }} | {{ item.weight }}</text>
</view>
</view>
<!-- 右侧状态 -->
<view class="item-status">
<text class="status-text" :class="getStatusClass(item.status)">
{{ getStatusText(item.status) }}
</text>
</view>
</view>
</view>
<!-- 空状态 -->
<view class="empty-state" v-if="returnList.length === 0">
<view class="empty-icon">📦</view>
<text class="empty-text">暂无归还记录</text>
</view>
</view>
</template>
<script setup>
import {
ref
} from 'vue'
// 模拟数据
const returnList = ref([{
id: 1,
expressCompany: '顺丰速运',
trackingNumber: 'SF1234567890123',
returnAddress: '北京市朝阳区建国门外大街1号',
returnTime: '2024-01-15 14:30:00',
packageType: '文件',
weight: '0.5kg',
status: 'completed'
},
{
id: 2,
expressCompany: '圆通快递',
trackingNumber: 'YT9876543210987',
returnAddress: '上海市浦东新区陆家嘴环路1000号',
returnTime: '2024-01-14 09:15:00',
packageType: '电子产品',
weight: '2.1kg',
status: 'processing'
}
])
// 获取状态样式类
const getStatusClass = (status) => {
const statusMap = {
'completed': 'status-completed',
'processing': 'status-processing',
'pending': 'status-pending'
}
return statusMap[status] || 'status-pending'
}
// 获取状态图标
const getStatusIcon = (status) => {
const iconMap = {
'completed': '✓',
'processing': '⏳',
'pending': '⏸'
}
return iconMap[status] || '⏸'
}
// 获取状态文本
const getStatusText = (status) => {
const textMap = {
'completed': '已完成',
'processing': '处理中',
'pending': '待处理'
}
return textMap[status] || '待处理'
}
// 点击列表项
const handleItemClick = (item) => {
console.log('点击了归还记录:', item)
// 跳转到详情页面
uni.navigateTo({
url: `/pages/expressReturn/detail?id=${item.id}`
})
}
</script>
<style lang="scss">
.express-return-container {
min-height: 100vh;
background-color: #f8f9fa;
padding: 20rpx;
}
.page-header {
margin-bottom: 40rpx;
padding: 20rpx 0;
}
.page-title {
display: block;
font-size: 36rpx;
font-weight: 600;
color: #2c3e50;
margin-bottom: 10rpx;
}
.page-subtitle {
display: block;
font-size: 28rpx;
color: #7f8c8d;
}
.list-container {
background-color: transparent;
}
.return-item {
display: flex;
align-items: center;
padding: 30rpx 24rpx;
background-color: #ffffff;
border-radius: 16rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
transition: background-color 0.2s ease;
&:last-child {
margin-bottom: 0;
}
&:active {
background-color: #f8f9fa;
}
}
.item-icon {
margin-right: 24rpx;
}
.icon-wrapper {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
font-weight: bold;
&.status-completed {
background-color: #e8f5e8;
color: #27ae60;
}
&.status-processing {
background-color: #fff3cd;
color: #f39c12;
}
&.status-pending {
background-color: #f8f9fa;
color: #6c757d;
}
}
.item-content {
flex: 1;
margin-right: 20rpx;
}
.item-header {
margin-bottom: 12rpx;
}
.express-company {
display: block;
font-size: 32rpx;
font-weight: 600;
color: #2c3e50;
margin-bottom: 8rpx;
}
.tracking-number {
display: block;
font-size: 26rpx;
color: #7f8c8d;
}
.item-details {
margin-bottom: 12rpx;
}
.return-address,
.return-time {
display: block;
font-size: 26rpx;
color: #34495e;
line-height: 1.5;
margin-bottom: 6rpx;
}
.item-footer {
margin-top: 8rpx;
}
.package-info {
font-size: 24rpx;
color: #95a5a6;
}
.item-status {
text-align: right;
}
.status-text {
font-size: 26rpx;
font-weight: 500;
padding: 8rpx 16rpx;
border-radius: 20rpx;
&.status-completed {
background-color: #e8f5e8;
color: #27ae60;
}
&.status-processing {
background-color: #fff3cd;
color: #f39c12;
}
&.status-pending {
background-color: #f8f9fa;
color: #6c757d;
}
}
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 120rpx 40rpx;
// background-color: #ffffff;
// border-radius: 16rpx;
// box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
}
.empty-icon {
font-size: 80rpx;
margin-bottom: 20rpx;
}
.empty-text {
font-size: 28rpx;
color: #7f8c8d;
}
</style>
+283 -302
View File
@@ -1,333 +1,314 @@
<template>
<view class="feedback-container">
<!-- 问题类型选择 -->
<view class="type-section">
<view class="section-title">问题类型</view>
<view class="type-grid">
<view
v-for="(type, index) in types"
:key="index"
class="type-item"
:class="{ active: selectedType === index }"
@click="selectType(index)"
>
{{ type }}
</view>
</view>
</view>
<view class="feedback-container">
<!-- 问题类型选择 -->
<view class="type-section">
<view class="section-title">问题类型</view>
<view class="type-grid">
<view v-for="(type, index) in types" :key="index" class="type-item"
:class="{ active: selectedType === index }" @click="selectType(index)">
{{ type }}
</view>
</view>
</view>
<!-- 问题描述 -->
<view class="description-section">
<view class="section-title">问题描述</view>
<textarea
class="description-input"
v-model="description"
placeholder="请详细描述您遇到的问题,以便我们更好地为您解决"
maxlength="500"
/>
<view class="word-count">{{ description.length }}/500</view>
</view>
<!-- 问题描述 -->
<view class="description-section">
<view class="section-title">问题描述</view>
<textarea class="description-input" v-model="description" placeholder="请详细描述您遇到的问题,以便我们更好地为您解决"
maxlength="500" />
<view class="word-count">{{ description.length }}/500</view>
</view>
<!-- 图片上传 -->
<view class="upload-section">
<view class="section-title">图片上传选填</view>
<view class="upload-grid">
<view
class="upload-item"
v-for="(img, index) in images"
:key="index"
>
<image :src="img" mode="aspectFill" />
<view class="delete-btn" @click="deleteImage(index)">×</view>
</view>
<view class="upload-btn" @click="chooseImage" v-if="images.length < 3">
<text class="plus">+</text>
<text class="tip">上传图片</text>
</view>
</view>
</view>
<!-- 图片上传 -->
<view class="upload-section">
<view class="section-title">图片上传选填</view>
<view class="upload-grid">
<view class="upload-item" v-for="(img, index) in images" :key="index">
<image :src="img" mode="aspectFill" />
<view class="delete-btn" @click="deleteImage(index)">×</view>
</view>
<view class="upload-btn" @click="chooseImage" v-if="images.length < 3">
<text class="plus">+</text>
<text class="tip">上传图片</text>
</view>
</view>
</view>
<!-- 联系方式 -->
<view class="contact-section">
<view class="section-title">联系方式</view>
<input
class="contact-input"
v-model="contact"
placeholder="请留下您的手机号,方便我们联系您"
type="number"
maxlength="11"
/>
</view>
<!-- 联系方式 -->
<view class="contact-section">
<view class="section-title">联系方式</view>
<input class="contact-input" v-model="contact" placeholder="请留下您的手机号,方便我们联系您" type="number"
maxlength="11" />
</view>
<!-- 提交按钮 -->
<view class="submit-section">
<button class="submit-btn" @click="submitFeedback">提交反馈</button>
</view>
</view>
<!-- 提交按钮 -->
<view class="submit-section">
<button class="submit-btn" @click="submitFeedback">提交反馈</button>
</view>
</view>
</template>
<script>
export default {
data() {
return {
types: ['设备故障', '收费问题', '使用建议', '其他'],
selectedType: -1,
description: '',
images: [],
contact: ''
}
},
methods: {
selectType(index) {
this.selectedType = index
},
chooseImage() {
uni.chooseImage({
count: 3 - this.images.length,
success: (res) => {
this.images = [...this.images, ...res.tempFilePaths]
}
})
},
deleteImage(index) {
this.images.splice(index, 1)
},
submitFeedback() {
if (this.selectedType === -1) {
uni.showToast({
title: '请选择问题类型',
icon: 'none'
})
return
}
if (!this.description.trim()) {
uni.showToast({
title: '请描述您的问题',
icon: 'none'
})
return
}
if (!this.contact) {
uni.showToast({
title: '请留下联系方式',
icon: 'none'
})
return
}
// 构建反馈数据
const feedbackData = {
type: this.types[this.selectedType],
content: this.description,
phone: this.contact,
images: this.images
}
// 调用提交接口
uni.request({
url: '/app/feedback/add',
method: 'POST',
data: feedbackData,
success: (res) => {
if (res.statusCode === 200) {
uni.showToast({
title: '提交成功',
icon: 'success'
})
// 清空表单
this.selectedType = -1
this.description = ''
this.contact = ''
this.images = []
} else {
uni.showToast({
title: '提交失败',
icon: 'none'
})
}
},
fail: () => {
uni.showToast({
title: '提交失败',
icon: 'none'
})
}
})
}
}
}
<script setup>
import {
ref
} from 'vue'
import {
URL
} from '../../config/url'
import {
addUserFeedback
} from '../../config/user'
// 响应式数据
const types = ref(['设备故障', '收费问题', '使用建议', '其他'])
const selectedType = ref(-1)
const paramsType = ref('')
const description = ref('')
const images = ref([])
const contact = ref('')
const apiUrl = URL
// 方法
const selectType = (index) => {
selectedType.value = index
}
const chooseImage = () => {
uni.chooseImage({
count: 3 - images.value.length,
success: (res) => {
images.value = [...images.value, ...res.tempFilePaths]
}
})
}
const deleteImage = (index) => {
images.value.splice(index, 1)
}
const submitFeedback = async () => {
if (selectedType.value === -1) {
uni.showToast({
title: '请选择问题类型',
icon: 'none'
})
return
}
if (!description.value.trim()) {
uni.showToast({
title: '请描述您的问题',
icon: 'none'
})
return
}
if (!contact.value) {
uni.showToast({
title: '请留下联系方式',
icon: 'none'
})
return
}
if (types.value[selectedType.value] == '设备故障' || types.value[selectedType.value] == '收费问题') {
paramsType.value = 'complain'
} else {
paramsType.value = 'suggestion'
}
// 构建反馈数据
const feedbackData = {
type: paramsType.value,
content: description.value,
phone: contact.value,
images: images.value
}
const res = await addUserFeedback(feedbackData);
if (res.code == 200) {
uni.showToast({
title: '反馈成功',
icon: 'success'
})
} else {
uni.showToast({
title: '反馈失败',
icon: 'none'
})
}
}
</script>
<style lang="scss" scoped>
.feedback-container {
min-height: 100vh;
background: #f8f8f8;
padding: 30rpx;
.feedback-container {
min-height: 100vh;
background: #f8f8f8;
padding: 30rpx;
.section-title {
font-size: 30rpx;
color: #333;
font-weight: 500;
margin-bottom: 20rpx;
}
.section-title {
font-size: 30rpx;
color: #333;
font-weight: 500;
margin-bottom: 20rpx;
}
.type-section {
background: #fff;
border-radius: 20rpx;
padding: 30rpx;
margin-bottom: 20rpx;
.type-section {
background: #fff;
border-radius: 20rpx;
padding: 30rpx;
margin-bottom: 20rpx;
.type-grid {
display: flex;
flex-wrap: wrap;
margin: 0 -10rpx;
.type-grid {
display: flex;
flex-wrap: wrap;
margin: 0 -10rpx;
.type-item {
width: calc(50% - 20rpx);
margin: 10rpx;
height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
background: #f5f5f5;
border-radius: 10rpx;
font-size: 28rpx;
color: #666;
transition: all 0.3s;
.type-item {
width: calc(50% - 20rpx);
margin: 10rpx;
height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
background: #f5f5f5;
border-radius: 10rpx;
font-size: 28rpx;
color: #666;
transition: all 0.3s;
&.active {
background: #E3F2FD;
color: #1976D2;
}
}
}
}
&.active {
background: #E3F2FD;
color: #1976D2;
}
}
}
}
.description-section {
background: #fff;
border-radius: 20rpx;
padding: 30rpx;
margin-bottom: 20rpx;
.description-section {
background: #fff;
border-radius: 20rpx;
padding: 30rpx;
margin-bottom: 20rpx;
.description-input {
width: 100%;
height: 240rpx;
background: #f8f8f8;
border-radius: 10rpx;
padding: 20rpx;
font-size: 28rpx;
color: #333;
box-sizing: border-box;
}
.description-input {
width: 100%;
height: 240rpx;
background: #f8f8f8;
border-radius: 10rpx;
padding: 20rpx;
font-size: 28rpx;
color: #333;
box-sizing: border-box;
}
.word-count {
text-align: right;
font-size: 24rpx;
color: #999;
margin-top: 10rpx;
}
}
.word-count {
text-align: right;
font-size: 24rpx;
color: #999;
margin-top: 10rpx;
}
}
.upload-section {
background: #fff;
border-radius: 20rpx;
padding: 30rpx;
margin-bottom: 20rpx;
.upload-section {
background: #fff;
border-radius: 20rpx;
padding: 30rpx;
margin-bottom: 20rpx;
.upload-grid {
display: flex;
flex-wrap: wrap;
.upload-grid {
display: flex;
flex-wrap: wrap;
.upload-item {
width: 200rpx;
height: 200rpx;
margin-right: 20rpx;
margin-bottom: 20rpx;
position: relative;
.upload-item {
width: 200rpx;
height: 200rpx;
margin-right: 20rpx;
margin-bottom: 20rpx;
position: relative;
image {
width: 100%;
height: 100%;
border-radius: 10rpx;
}
image {
width: 100%;
height: 100%;
border-radius: 10rpx;
}
.delete-btn {
position: absolute;
right: -10rpx;
top: -10rpx;
width: 40rpx;
height: 40rpx;
background: rgba(0,0,0,0.5);
color: #fff;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
}
}
.delete-btn {
position: absolute;
right: -10rpx;
top: -10rpx;
width: 40rpx;
height: 40rpx;
background: rgba(0, 0, 0, 0.5);
color: #fff;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
}
}
.upload-btn {
width: 200rpx;
height: 200rpx;
background: #f5f5f5;
border-radius: 10rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: #999;
.upload-btn {
width: 200rpx;
height: 200rpx;
background: #f5f5f5;
border-radius: 10rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: #999;
.plus {
font-size: 60rpx;
line-height: 1;
margin-bottom: 10rpx;
}
.plus {
font-size: 60rpx;
line-height: 1;
margin-bottom: 10rpx;
}
.tip {
font-size: 24rpx;
}
}
}
}
.tip {
font-size: 24rpx;
}
}
}
}
.contact-section {
background: #fff;
border-radius: 20rpx;
padding: 30rpx;
margin-bottom: 40rpx;
.contact-section {
background: #fff;
border-radius: 20rpx;
padding: 30rpx;
margin-bottom: 40rpx;
.contact-input {
width: 100%;
height: 80rpx;
background: #f8f8f8;
border-radius: 10rpx;
padding: 0 20rpx;
font-size: 28rpx;
color: #333;
box-sizing: border-box;
}
}
.contact-input {
width: 100%;
height: 80rpx;
background: #f8f8f8;
border-radius: 10rpx;
padding: 0 20rpx;
font-size: 28rpx;
color: #333;
box-sizing: border-box;
}
}
.submit-section {
padding: 0 40rpx;
.submit-section {
padding: 0 40rpx;
.submit-btn {
width: 100%;
height: 88rpx;
background: #1976D2;
color: #fff;
border-radius: 44rpx;
font-size: 32rpx;
font-weight: 500;
display: flex;
align-items: center;
justify-content: center;
.submit-btn {
width: 100%;
height: 88rpx;
background: #1976D2;
color: #fff;
border-radius: 44rpx;
font-size: 32rpx;
font-weight: 500;
display: flex;
align-items: center;
justify-content: center;
&:active {
transform: scale(0.98);
}
}
}
}
</style>
&:active {
transform: scale(0.98);
}
}
}
}
</style>
+247 -236
View File
@@ -9,23 +9,23 @@
<image v-else class="avatar" src="@/static/user-active.png" mode="aspectFill"></image>
<view class="avatar-badge" v-if="userInfo.isAdmin"></view>
</view>
<view class="user-details" v-if="userInfo">
<view class="username">{{ userInfo.nickName }}</view>
<view class="user-id">{{ userInfo.phone ? maskPhone(userInfo.phone) : '未绑定手机号' }}</view>
</view>
<view class="user-details" v-else>
<view class="username">点击登录</view>
<view class="user-id">授权登录后享受更多服务</view>
</view>
<view class="edit-profile">
<uni-icons type="right" size="16" color="#999"></uni-icons>
</view>
</view>
</view>
<!-- 余额卡片 -->
<view class="balance-card">
<view class="balance-info">
@@ -36,7 +36,7 @@
<text>提现</text>
</view>
</view>
<!-- 功能区域列表 -->
<view class="function-list">
<!-- 订单中心 -->
@@ -51,7 +51,7 @@
<uni-icons type="right" size="16" color="#999"></uni-icons>
</view>
</view>
<!-- 投诉建议 -->
<view class="function-item" @click="navigateTo('/pages/feedback/index')">
<view class="item-left">
@@ -64,7 +64,7 @@
<uni-icons type="right" size="16" color="#999"></uni-icons>
</view>
</view>
<!-- 帮助中心 -->
<view class="function-item" @click="navigateTo('/pages/help/index')">
<view class="item-left">
@@ -77,8 +77,19 @@
<uni-icons type="right" size="16" color="#999"></uni-icons>
</view>
</view>
<!-- <view class="function-item" @click="navigateTo('/pages/expressReturn/index')">
<view class="item-left">
<view class="item-icon">
<image src="/static/express.png" mode="aspectFit"></image>
</view>
<text class="item-title">快递归还</text>
</view>
<view class="item-right">
<uni-icons type="right" size="16" color="#999"></uni-icons>
</view>
</view> -->
</view>
<!--
<view class="section-title">设置</view>
@@ -95,8 +106,8 @@
<uni-icons type="right" size="16" color="#999"></uni-icons>
</view>
</view> -->
<!-- <u-popup ref="authPopup" mode="center" border-radius="15" width="600rpx" @open="onPopupOpen" @close="onPopupClose">
<!-- <u-popup ref="authPopup" mode="center" border-radius="15" width="600rpx" @open="onPopupOpen" @close="onPopupClose">
<view class="auth-popup">
<view class="auth-title">授权登录</view>
<view class="auth-desc">获取您的微信头像昵称等公开信息</view>
@@ -144,14 +155,14 @@
const res = await getUserInfo();
console.log('User info response:', res);
if (res.code == 200) {
// 保存openId
if (res.data.openId) {
openId.value = res.data.openId;
uni.setStorageSync('openId', res.data.openId);
}
// 更新用户信息
userInfo.value = {
nickName: res.data.nickname,
@@ -159,7 +170,7 @@
avatar: res.data.iconUrl,
isAdmin: res.data.isAdmin
};
uni.setStorageSync('userInfo', userInfo.value);
deposit.value = res.data.balanceAmount || '0.00';
}
@@ -178,7 +189,7 @@
url
});
};
// 处理提现按钮点击
const handleWithdraw = () => {
navigateTo('/pages/deposit/index');
@@ -194,7 +205,7 @@
// navigateTo('/pages/profile/edit');
}
};
// 打开授权弹窗
const openAuthPopup = () => {
if (authPopup.value) {
@@ -202,14 +213,14 @@
isPopupVisible.value = true;
}
};
// 弹窗打开事件处理
const onPopupOpen = () => {
console.log('授权弹窗已打开');
isPopupVisible.value = true;
// 这里可以添加弹窗打开后的逻辑
};
// 弹窗关闭事件处理
const onPopupClose = () => {
console.log('授权弹窗已关闭');
@@ -224,7 +235,7 @@
title: '获取中...',
mask: true
});
wx.getUserProfile({
desc: '用于完善会员资料',
success: (res) => {
@@ -244,7 +255,7 @@
}
});
// #endif
// #ifndef MP-WEIXIN
uni.showToast({
title: '请在微信小程序中使用此功能',
@@ -263,10 +274,10 @@
nickName: wxUserInfo.nickName,
avatar: wxUserInfo.avatarUrl
};
userInfo.value = updatedInfo;
uni.setStorageSync('userInfo', updatedInfo);
// 这里可以添加调用后端API更新用户信息的代码
// const updateRes = await updateUserInfoApi({
// openId: openId.value,
@@ -274,12 +285,12 @@
// avatarUrl: wxUserInfo.avatarUrl,
// gender: wxUserInfo.gender
// });
uni.showToast({
title: '信息更新成功',
icon: 'success'
});
// 更新完成后重新获取用户信息
getInfo();
} catch (error) {
@@ -317,232 +328,232 @@
// 手机号掩码函数
function maskPhone(phone) {
if (!phone) return '';
// 只处理11位手机号
return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2');
if (!phone) return '';
// 只处理11位手机号
return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2');
}
</script>
<style lang="scss" scoped>
.profile-container {
min-height: 100vh;
background-color: #f5f7fa;
padding-bottom: env(safe-area-inset-bottom);
}
/* Header Section */
.header-section {
padding: 40rpx;
background: linear-gradient(135deg, #4facfe, #00f2fe);
position: relative;
border-radius: 0 0 30rpx 30rpx;
margin-bottom: 20rpx;
box-shadow: 0 10rpx 30rpx rgba(79, 172, 254, 0.2);
}
.user-profile {
display: flex;
align-items: center;
padding: 20rpx 0;
}
.avatar-container {
position: relative;
margin-right: 30rpx;
}
.avatar {
width: 120rpx;
height: 120rpx;
border-radius: 60rpx;
border: 4rpx solid rgba(255, 255, 255, 0.6);
box-shadow: 0 8rpx 20rpx rgba(0, 0, 0, 0.1);
}
.avatar-badge {
position: absolute;
bottom: 0;
right: 0;
width: 36rpx;
height: 36rpx;
border-radius: 18rpx;
background-color: #ff9500;
border: 4rpx solid white;
}
.user-details {
flex: 1;
}
.username {
font-size: 36rpx;
font-weight: 600;
color: white;
margin-bottom: 8rpx;
}
.user-id {
font-size: 26rpx;
color: rgba(255, 255, 255, 0.8);
}
.edit-profile {
background-color: rgba(255, 255, 255, 0.2);
padding: 10rpx;
border-radius: 50%;
}
/* Balance Card */
.balance-card {
margin: 30rpx;
background: white;
border-radius: 20rpx;
padding: 30rpx;
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.05);
}
.balance-label {
font-size: 28rpx;
color: #666;
margin-bottom: 10rpx;
}
.balance-amount {
font-size: 48rpx;
font-weight: 600;
color: #4facfe;
}
.action-button {
background: linear-gradient(135deg, #4facfe, #00f2fe);
border-radius: 40rpx;
height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
padding: 0 40rpx;
color: white;
font-weight: 500;
font-size: 30rpx;
box-shadow: 0 8rpx 16rpx rgba(79, 172, 254, 0.2);
&:active {
opacity: 0.9;
transform: scale(0.98);
.profile-container {
min-height: 100vh;
background-color: #f5f7fa;
padding-bottom: env(safe-area-inset-bottom);
}
text {
margin-right: 10rpx;
/* Header Section */
.header-section {
padding: 40rpx;
background: linear-gradient(135deg, #4facfe, #00f2fe);
position: relative;
border-radius: 0 0 30rpx 30rpx;
margin-bottom: 20rpx;
box-shadow: 0 10rpx 30rpx rgba(79, 172, 254, 0.2);
}
}
/* Function List */
.function-list {
margin: 30rpx;
background: white;
border-radius: 20rpx;
overflow: hidden;
box-shadow: 0 6rpx 16rpx rgba(0, 0, 0, 0.04);
}
.function-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 32rpx 30rpx;
border-bottom: 1rpx solid #f0f0f0;
&:last-child {
border-bottom: none;
.user-profile {
display: flex;
align-items: center;
padding: 20rpx 0;
}
&:active {
background-color: #f9f9f9;
.avatar-container {
position: relative;
margin-right: 30rpx;
}
}
.item-left {
display: flex;
align-items: center;
}
.item-icon {
width: 48rpx;
height: 48rpx;
margin-right: 24rpx;
display: flex;
align-items: center;
justify-content: center;
image {
width: 40rpx;
height: 40rpx;
.avatar {
width: 120rpx;
height: 120rpx;
border-radius: 60rpx;
border: 4rpx solid rgba(255, 255, 255, 0.6);
box-shadow: 0 8rpx 20rpx rgba(0, 0, 0, 0.1);
}
}
.item-title {
font-size: 30rpx;
color: #333;
font-weight: 500;
}
.item-right {
display: flex;
align-items: center;
}
/* Auth Popup */
.auth-popup {
background-color: white;
width: 100%;
padding: 40rpx;
border-radius: 15rpx;
}
.auth-title {
font-size: 34rpx;
font-weight: 600;
color: #333;
text-align: center;
margin-bottom: 20rpx;
}
.auth-desc {
font-size: 28rpx;
color: #666;
text-align: center;
margin-bottom: 40rpx;
}
.auth-buttons {
display: flex;
justify-content: space-between;
}
.cancel-btn,
.confirm-btn {
width: 240rpx;
height: 80rpx;
line-height: 80rpx;
text-align: center;
border-radius: 40rpx;
font-size: 28rpx;
&::after {
border: none;
.avatar-badge {
position: absolute;
bottom: 0;
right: 0;
width: 36rpx;
height: 36rpx;
border-radius: 18rpx;
background-color: #ff9500;
border: 4rpx solid white;
}
}
.cancel-btn {
background-color: #f0f0f0;
color: #666;
}
.user-details {
flex: 1;
}
.confirm-btn {
background: linear-gradient(135deg, #4facfe, #00f2fe);
color: white;
}
.username {
font-size: 36rpx;
font-weight: 600;
color: white;
margin-bottom: 8rpx;
}
.user-id {
font-size: 26rpx;
color: rgba(255, 255, 255, 0.8);
}
.edit-profile {
background-color: rgba(255, 255, 255, 0.2);
padding: 10rpx;
border-radius: 50%;
}
/* Balance Card */
.balance-card {
margin: 30rpx;
background: white;
border-radius: 20rpx;
padding: 30rpx;
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.05);
}
.balance-label {
font-size: 28rpx;
color: #666;
margin-bottom: 10rpx;
}
.balance-amount {
font-size: 48rpx;
font-weight: 600;
color: #4facfe;
}
.action-button {
background: linear-gradient(135deg, #4facfe, #00f2fe);
border-radius: 40rpx;
height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
padding: 0 40rpx;
color: white;
font-weight: 500;
font-size: 30rpx;
box-shadow: 0 8rpx 16rpx rgba(79, 172, 254, 0.2);
&:active {
opacity: 0.9;
transform: scale(0.98);
}
text {
margin-right: 10rpx;
}
}
/* Function List */
.function-list {
margin: 30rpx;
background: white;
border-radius: 20rpx;
overflow: hidden;
box-shadow: 0 6rpx 16rpx rgba(0, 0, 0, 0.04);
}
.function-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 32rpx 30rpx;
border-bottom: 1rpx solid #f0f0f0;
&:last-child {
border-bottom: none;
}
&:active {
background-color: #f9f9f9;
}
}
.item-left {
display: flex;
align-items: center;
}
.item-icon {
width: 48rpx;
height: 48rpx;
margin-right: 24rpx;
display: flex;
align-items: center;
justify-content: center;
image {
width: 40rpx;
height: 40rpx;
}
}
.item-title {
font-size: 30rpx;
color: #333;
font-weight: 500;
}
.item-right {
display: flex;
align-items: center;
}
/* Auth Popup */
.auth-popup {
background-color: white;
width: 100%;
padding: 40rpx;
border-radius: 15rpx;
}
.auth-title {
font-size: 34rpx;
font-weight: 600;
color: #333;
text-align: center;
margin-bottom: 20rpx;
}
.auth-desc {
font-size: 28rpx;
color: #666;
text-align: center;
margin-bottom: 40rpx;
}
.auth-buttons {
display: flex;
justify-content: space-between;
}
.cancel-btn,
.confirm-btn {
width: 240rpx;
height: 80rpx;
line-height: 80rpx;
text-align: center;
border-radius: 40rpx;
font-size: 28rpx;
&::after {
border: none;
}
}
.cancel-btn {
background-color: #f0f0f0;
color: #666;
}
.confirm-btn {
background: linear-gradient(135deg, #4facfe, #00f2fe);
color: white;
}
</style>
-446
View File
@@ -1,446 +0,0 @@
<template>
<view class="return-container">
<!-- 订单信息卡片 -->
<view class="order-card">
<view class="order-header">
<text class="title">{{ orderStatusText }}</text>
<text class="order-no">订单号{{ orderInfo.orderNo || '-' }}</text>
</view>
<view class="device-info">
<view class="device-left">
<view class="device-name">共享风扇</view>
<view class="device-id">设备号{{ orderInfo.deviceNo || '-' }}</view>
</view>
<view class="device-right">
<view class="payment-badge wx-score" v-if="orderInfo.payWay === 'wx_score_pay'">
<image src="/static/images/wxpayflag.png" mode="aspectFit" class="badge-icon"></image>
<view class="badge-text">
<text>微信支付分</text>
<text class="divider">|</text>
<text class="highlight">免押租借</text>
</view>
</view>
<!-- 会员订单标识 -->
<view class="payment-badge member" v-else-if="orderInfo.payWay === 'wx_member_pay'">
<text class="badge-text">会员订单</text>
</view>
<!-- 微信支付押金标识 -->
<view class="payment-badge deposit" v-else-if="orderInfo.payWay === 'wx_pay'">
<text class="badge-text">押金租借</text>
</view>
</view>
</view>
<view class="time-info">
<!-- <view class="time-item">
<text class="label">创建时间</text>
<text class="value">{{ orderInfo.createTime || '-' }}</text>
</view> -->
<view class="time-item">
<text class="label">开始时间</text>
<text class="value">{{ orderInfo.startTime || '-' }}</text>
</view>
<view class="time-item" v-if="orderInfo.endTime">
<text class="label">结束时间</text>
<text class="value">{{ orderInfo.endTime }}</text>
</view>
<view class="time-item" v-if="orderInfo.phone">
<text class="label">联系电话</text>
<text class="value">{{ orderInfo.phone }}</text>
</view>
</view>
</view>
<!-- 费用信息卡片 -->
<view class="notice-card">
<view class="notice-title">费用信息</view>
<view class="notice-list">
<view class="notice-item" v-if="orderInfo.depositAmount">
<view class="dot"></view>
<text>押金{{ orderInfo.depositAmount }}</text>
</view>
<view class="notice-item" v-if="orderInfo.packageTime && orderInfo.packagePrice">
<view class="dot"></view>
<text>套餐{{ orderInfo.packagePrice }} / {{ formatTime(orderInfo.packageTime) }}</text>
</view>
<view class="notice-item">
<view class="dot"></view>
<text>合计{{ orderInfo.payAmount || 0 }}</text>
</view>
</view>
</view>
<!-- 底部操作栏 -->
<!-- <view class="bottom-bar">
<view class="action-item secondary" v-if="orderInfo.orderStatus == 'waiting_for_payment'" @click="handleCancelOrder">取消订单</view>
<view class="action-item primary" v-if="orderInfo.orderStatus == 'waiting_for_payment'" @click="handlePayment">立即支付</view>
<view class="action-item primary" v-else-if="orderInfo.orderStatus == 'in_used'" @click="navigateToReturn">归还设备</view>
</view> -->
</view>
</template>
<script setup>
import { ref, computed } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
import { queryById, cancelOrder, confirmPaymentAndRent } from '@/config/user.js';
const orderId = ref('');
const orderInfo = ref({});
const orderStatusText = computed(() => {
const status = orderInfo.value.orderStatus;
switch(status) {
case 'waiting_for_payment': return '待支付';
case 'in_used': return '使用中';
case 'used_done': return '已完成';
case 'order_cancelled': return '已取消';
default: return '使用中';
}
});
onLoad(async (options) => {
if (options && options.orderId) {
orderId.value = options.orderId;
await loadOrderDetails();
} else {
uni.showToast({
title: '订单信息不存在',
icon: 'none'
});
setTimeout(() => {
uni.navigateBack();
}, 1500);
}
});
const loadOrderDetails = async () => {
try {
uni.showLoading({ title: '加载中' });
const res = await queryById(orderId.value);
if (res.code === 200 && res.data) {
orderInfo.value = res.data;
if (orderInfo.value.createTime) {
orderInfo.value.createTime = formatDateTime(new Date(orderInfo.value.createTime));
}
if (orderInfo.value.startTime) {
orderInfo.value.startTime = formatDateTime(new Date(orderInfo.value.startTime));
}
if (orderInfo.value.endTime) {
orderInfo.value.endTime = formatDateTime(new Date(orderInfo.value.endTime));
}
} else {
throw new Error('获取订单详情失败');
}
uni.hideLoading();
} catch (error) {
uni.hideLoading();
uni.showToast({
title: error.message || '获取订单详情失败',
icon: 'none'
});
}
};
const formatDateTime = (date) => {
const year = date.getFullYear();
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const day = date.getDate().toString().padStart(2, '0');
const hour = date.getHours().toString().padStart(2, '0');
const minute = date.getMinutes().toString().padStart(2, '0');
return `${year}-${month}-${day} ${hour}:${minute}`;
};
const formatTime = (minutes) => {
if (!minutes) return '';
const mins = parseInt(minutes);
if (mins < 60) {
return `${mins}分钟`;
} else {
const hours = Math.floor(mins / 60);
const remainingMins = mins % 60;
return remainingMins > 0 ? `${hours}小时${remainingMins}分钟` : `${hours}小时`;
}
};
const handleCancelOrder = () => {
uni.showModal({
title: '确认取消',
content: '确定要取消此订单吗?',
success: async (res) => {
if (res.confirm) {
try {
uni.showLoading({ title: '处理中' });
const result = await cancelOrder({ orderId: orderId.value });
if (result.code === 200) {
uni.hideLoading();
uni.showToast({ title: '订单已取消', icon: 'success' });
await loadOrderDetails();
} else {
throw new Error(result.msg || '取消订单失败');
}
} catch (error) {
uni.hideLoading();
uni.showToast({ title: error.message || '取消订单失败', icon: 'none' });
}
}
}
});
};
const handlePayment = async () => {
try {
uni.showLoading({ title: '处理中' });
const res = await confirmPaymentAndRent(orderId.value);
if (res.code === 200) {
uni.hideLoading();
uni.showToast({ title: '支付成功', icon: 'success' });
await loadOrderDetails();
} else {
throw new Error(res.msg || '支付失败');
}
} catch (error) {
uni.hideLoading();
uni.showToast({ title: error.message || '支付失败', icon: 'none' });
}
};
const navigateToReturn = () => {
uni.navigateTo({
url: `/pages/return/index?deviceId=${orderInfo.value.deviceNo}&orderId=${orderId.value}`
});
};
</script>
<style lang="scss" scoped>
.return-container {
min-height: 100vh;
background: #f7f8fa;
padding: 30rpx;
padding-bottom: 180rpx;
box-sizing: border-box;
.order-card {
background: #fff;
border-radius: 20rpx;
padding: 30rpx;
margin-bottom: 30rpx;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
.order-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
padding-bottom: 16rpx;
border-bottom: 1rpx solid #f0f0f0;
.title {
font-size: 30rpx;
font-weight: bold;
color: #333;
}
.order-no {
font-size: 24rpx;
color: #999;
}
}
.device-info {
margin-bottom: 24rpx;
display: flex;
justify-content: space-between;
align-items: flex-start;
.device-left {
flex: 1;
margin-right: 20rpx;
.device-name {
font-size: 32rpx;
font-weight: 500;
color: #333;
margin-bottom: 8rpx;
}
.device-id {
font-size: 26rpx;
color: #999;
margin-bottom: 0;
}
}
.device-right {
.payment-badge {
display: inline-flex;
align-items: center;
padding: 6rpx 12rpx;
border-radius: 8rpx;
white-space: nowrap;
&.wx-score {
background: rgba(7, 193, 96, 0.08);
.badge-icon {
width: 32rpx;
height: 26rpx;
margin-right: 8rpx;
}
.badge-text {
font-size: 22rpx;
color: #07c160;
display: flex;
align-items: center;
.divider {
margin: 0 6rpx;
}
.highlight {
font-weight: 500;
}
}
}
&.member {
background: rgba(25, 118, 210, 0.08);
.badge-text {
font-size: 22rpx;
color: #1976D2;
font-weight: 500;
}
}
&.deposit {
background: #f5f5f5;
.badge-text {
font-size: 22rpx;
color: #666;
font-weight: 500;
}
}
}
}
}
.time-info {
background: #f9f9f9;
border-radius: 16rpx;
padding: 20rpx;
.time-item {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16rpx;
&:last-child {
margin-bottom: 0;
}
.label {
font-size: 26rpx;
color: #666;
}
.value {
font-size: 26rpx;
color: #333;
&.highlight {
color: #ff6b6b;
font-weight: bold;
}
}
}
}
}
.notice-card {
background: #fff;
border-radius: 20rpx;
padding: 30rpx;
margin-bottom: 30rpx;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
.notice-title {
font-size: 30rpx;
font-weight: bold;
color: #333;
margin-bottom: 20rpx;
}
.notice-list {
.notice-item {
display: flex;
align-items: flex-start;
margin-bottom: 16rpx;
&:last-child {
margin-bottom: 0;
}
.dot {
width: 12rpx;
height: 12rpx;
background: #07c160;
border-radius: 50%;
margin-top: 10rpx;
margin-right: 16rpx;
flex-shrink: 0;
}
text {
font-size: 26rpx;
color: #666;
line-height: 1.5;
}
}
}
}
.bottom-bar {
position: fixed;
left: 0;
right: 0;
bottom: 0;
padding: 20rpx 30rpx;
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
background: #fff;
box-shadow: 0 -4rpx 16rpx rgba(0, 0, 0, 0.04);
z-index: 10;
display: flex;
justify-content: space-between;
gap: 20rpx;
.action-item {
height: 88rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 30rpx;
border-radius: 44rpx;
flex: 1;
&.primary {
background: #07c160;
color: #fff;
&:active {
opacity: 0.8;
}
}
&.secondary {
background: #f5f5f5;
color: #333;
border: 1rpx solid #e0e0e0;
&:active {
opacity: 0.8;
}
}
}
}
}
</style>
+1 -1
View File
@@ -309,7 +309,7 @@
// 跳转到订单详情页
const navigateToDetails = (order) => {
uni.navigateTo({
url: `/pages/order/details?orderId=${order.orderId || order.orderNo}`
url: `/pages/return/index?orderId=${order.orderId || order.orderNo}&deviceId=${order.deviceId}`
});
};
+3 -3
View File
@@ -69,9 +69,9 @@
<view class="notice-card">
<view class="card-title">退款说明</view>
<view class="notice-content">
<text>1. 押金剩余金额需要您手动申请提现</text>
<text>2. 提现申请提交后将在1-3个工作日内退还到原支付账户</text>
<text>3. 如有疑问请联系客服</text>
<view>1. 押金剩余金额需要您手动申请提现</view>
<view>2. 提现申请提交后将在1-3个工作日内退还到原支付账户</view>
<view>3. 如有疑问请联系客服</view>
</view>
</view>
+1 -1
View File
@@ -306,7 +306,7 @@ export default {
.button-group {
margin-top: 30px;
display: flex;
flex-direction: column;
// flex-direction: column;
gap: 16px;
.primary-btn {
+217 -46
View File
@@ -4,7 +4,7 @@
<view class="order-card">
<view class="order-header">
<text class="title">{{ getOrderStatusText() }}</text>
<text class="order-no">订单号{{ orderInfo.orderNo }}</text>
<text class="order-no">订单号{{ orderInfo.orderNo || '-' }}</text>
</view>
<view class="device-info">
@@ -24,8 +24,12 @@
<text class="highlight">免押租借</text>
</view>
</view>
<!-- 押金租借标识 -->
<view class="payment-badge deposit" v-else>
<!-- 会员订单标识 -->
<view class="payment-badge member" v-else-if="orderInfo.payWay == 'wx_member_pay'">
<text class="badge-text">会员订单</text>
</view>
<!-- 微信支付押金标识 -->
<view class="payment-badge deposit" v-else-if="orderInfo.payWay == 'wx_pay'">
<text class="badge-text">押金租借</text>
</view>
</view>
@@ -36,14 +40,22 @@
<text class="label">开始时间</text>
<text class="value">{{ orderInfo.startTime }}</text>
</view>
<view class="time-item">
<view class="time-item" v-if="orderInfo.endTime">
<text class="label">结束时间</text>
<text class="value">{{ orderInfo.endTime }}</text>
</view>
<view class="time-item" v-if="orderInfo.orderStatus === 'in_used'">
<text class="label">已使用时长</text>
<text class="value highlight">{{ orderInfo.usedTime }}</text>
</view>
<view class="time-item">
<view class="time-item" v-if="orderInfo.orderStatus === 'in_used'">
<text class="label">当前费用</text>
<text class="value">{{ orderInfo.currentFee }}</text>
</view>
<view class="time-item" v-if="orderInfo.phone">
<text class="label">联系电话</text>
<text class="value">{{ orderInfo.phone }}</text>
</view>
</view>
<!-- 调试信息(仅在开发环境显示) -->
@@ -55,8 +67,27 @@
</view>
</view>
<!-- 费用信息卡片 -->
<view class="notice-card" v-if="orderInfo.depositAmount || orderInfo.packageTime">
<view class="notice-title">费用信息</view>
<view class="notice-list">
<view class="notice-item" v-if="orderInfo.depositAmount">
<view class="dot"></view>
<text>押金{{ orderInfo.depositAmount }}</text>
</view>
<view class="notice-item" v-if="orderInfo.packageTime && orderInfo.packagePrice">
<view class="dot"></view>
<text>套餐{{ orderInfo.packagePrice }} / {{ formatTime(orderInfo.packageTime) }}</text>
</view>
<view class="notice-item">
<view class="dot"></view>
<text>合计{{ orderInfo.payAmount || 0 }}</text>
</view>
</view>
</view>
<!-- 归还说明 -->
<view class="notice-card">
<view class="notice-card" v-if="orderInfo.orderStatus === 'in_used'">
<view class="notice-title">归还说明</view>
<view class="notice-list">
<view class="notice-item">
@@ -78,17 +109,36 @@
</view>
</view>
<!-- <view class="" v-if="orderInfo.orderStatus === 'in_used'" @click="expressRetrunOrder">
周边无可归还场地点这里
</view> -->
<!-- 底部操作栏 -->
<view class="bottom-bar">
<view class="action-item secondary" @click="checkReturnStatus">刷新状态</view>
<view class="action-item primary" @click="goToHome">返回首页</view>
<!-- 使用中状态显示归还相关操作 -->
<view v-if="orderInfo.orderStatus === 'in_used'" class="action-item secondary" @click="checkReturnStatus">
刷新状态</view>
<view v-if="orderInfo.orderStatus === 'in_used'" class="action-item primary" @click="goToHome">返回首页</view>
<!-- 已完成状态显示查看详情和返回首页 -->
<view v-if="orderInfo.orderStatus === 'used_done' || orderInfo.orderStatus === 'used_down'"
class="action-item secondary" @click="goToHome">返回首页</view>
<view v-if="orderInfo.orderStatus === 'used_done' || orderInfo.orderStatus === 'used_down'"
class="action-item primary" @click="viewOrderDetails">查看详情</view>
<!-- 待支付状态显示支付和取消操作 -->
<view v-if="orderInfo.orderStatus === 'waiting_for_payment'" class="action-item secondary"
@click="handleCancelOrder">取消订单</view>
<view v-if="orderInfo.orderStatus === 'waiting_for_payment'" class="action-item primary"
@click="handlePayment">立即支付</view>
</view>
</view>
</template>
<script>
import {
queryById
queryById,
cancelOrder
} from '@/config/user.js'
import {
URL
@@ -105,8 +155,15 @@
usedTime: '0分钟',
currentFee: '0.00',
orderStatus: 'in_used', // 默认状态为使用中
payWay: '' ,// 新增支付方式字段
orderNo:''
payWay: '', // 新增支付方式字段
orderNo: '',
// 新增费用相关字段
depositAmount: '',
packageTime: '',
packagePrice: '',
payAmount: '0.00',
endTime: '',
phone: ''
},
timer: null,
statusCheckTimer: null,
@@ -125,7 +182,7 @@
// 获取传递的参数
this.orderInfo.orderId = options.orderId || ''
this.deviceId = options.deviceNo || options.deviceId || ''
console.log(`初始化参数: orderId=${this.orderInfo.orderId}, deviceId=${this.deviceId}`)
@@ -135,32 +192,6 @@
} else if (this.orderInfo.orderId) {
// 获取订单详情
this.getOrderDetails()
// 启动定时器更新使用时长
this.startTimer()
// 启动状态检查定时器
this.startStatusCheckTimer()
// 记录当前活跃订单ID
uni.setStorageSync('activeOrderId', this.orderInfo.orderId)
// 如果订单ID有效,将订单添加到全局监控服务
try {
if (this.$orderMonitor) {
// 先确保移除之前的监控(防止重复添加)
this.$orderMonitor.removeOrder({
orderId: this.orderInfo.orderId
})
// 添加到监控队列,明确指定为归还页面
this.$orderMonitor.addOrder({
orderId: this.orderInfo.orderId
}, 'return')
console.log('订单已添加到监控队列:', this.orderInfo.orderId)
} else {
console.warn('$orderMonitor 未定义,无法添加订单到监控队列')
}
} catch (error) {
console.error('添加订单到监控队列失败:', error)
}
} else {
// 缺少必要参数
uni.showToast({
@@ -265,9 +296,9 @@
'payment_successful': '支付成功',
'in_used': '使用中',
'payment_failed': '支付失败',
'order_cancelled': '订单取消',
'used_done': '订单完成',
'used_down': '订单完成'
'order_cancelled': '取消',
'used_done': '完成',
'used_down': '完成'
}
return statusMap[this.orderInfo.orderStatus] || '使用中'
@@ -321,6 +352,36 @@
// 更新后检查是否成功设置了startTime
console.log('更新后的开始时间:', this.orderInfo.startTime)
// 只有使用中的订单才启动定时器和监控
if (this.orderInfo.orderStatus === 'in_used') {
// 启动定时器更新使用时长
this.startTimer()
// 启动状态检查定时器
this.startStatusCheckTimer()
// 记录当前活跃订单ID
uni.setStorageSync('activeOrderId', this.orderInfo.orderId)
// 如果订单ID有效,将订单添加到全局监控服务
try {
if (this.$orderMonitor) {
// 先确保移除之前的监控(防止重复添加)
this.$orderMonitor.removeOrder({
orderId: this.orderInfo.orderId
})
// 添加到监控队列,明确指定为归还页面
this.$orderMonitor.addOrder({
orderId: this.orderInfo.orderId
}, 'return')
console.log('订单已添加到监控队列:', this.orderInfo.orderId)
} else {
console.warn('$orderMonitor 未定义,无法添加订单到监控队列')
}
} catch (error) {
console.error('添加订单到监控队列失败:', error)
}
}
} else {
throw new Error(result.msg || '获取订单详情失败')
}
@@ -393,7 +454,28 @@
this.orderInfo.startTime = '未知';
}
}
if(orderData.orderNo){
// 更新费用相关信息
if (orderData.depositAmount) {
this.orderInfo.depositAmount = orderData.depositAmount;
}
if (orderData.packageTime) {
this.orderInfo.packageTime = orderData.packageTime;
}
if (orderData.packagePrice) {
this.orderInfo.packagePrice = orderData.packagePrice;
}
if (orderData.payAmount) {
this.orderInfo.payAmount = orderData.payAmount;
}
if (orderData.endTime) {
this.orderInfo.endTime = orderData.endTime;
}
if (orderData.phone) {
this.orderInfo.phone = orderData.phone;
}
if (orderData.orderNo) {
this.orderInfo.orderNo = orderData.orderNo;
}
@@ -520,10 +602,6 @@
// 获取详细订单信息
this.getOrderDetails()
// 启动定时器
this.startTimer()
// 启动状态检查定时器
this.startStatusCheckTimer()
} else {
throw new Error('未找到使用中的订单')
}
@@ -560,6 +638,89 @@
uni.reLaunch({
url: '/pages/index/index'
})
},
// 查看订单详情
viewOrderDetails() {
uni.navigateTo({
url: `/pages/order/return-success?orderId=${this.orderInfo.orderId}`
})
},
expressRetrunOrder() {
uni.showLoading();
setTimeout(() => {
uni.hideLoading();
uni.showModal({
title: '提示',
content: '计费暂停,是否前往填写归还信息',
confirmText: '立即前往',
cancelText: '稍后填写',
success: (res) => {
if (res.confirm) {
uni.navigateTo({
url: `/pages/expressReturn/addExpressReturn?orderId=${this.orderInfo.orderId}`
})
}
}
})
}, 3000)
},
// 取消订单
handleCancelOrder() {
uni.showModal({
title: '确认取消',
content: '确定要取消此订单吗?',
success: async (res) => {
if (res.confirm) {
try {
uni.showLoading({
title: '处理中'
});
const result = await cancelOrder({
orderId: this.orderInfo.orderId
});
if (result.code === 200) {
uni.hideLoading();
uni.showToast({
title: '订单已取消',
icon: 'success'
});
await this.getOrderDetails();
} else {
throw new Error(result.msg || '取消订单失败');
}
} catch (error) {
uni.hideLoading();
uni.showToast({
title: error.message || '取消订单失败',
icon: 'none'
});
}
}
}
});
},
// 立即支付
handlePayment() {
uni.navigateTo({
url: `/pages/order/payment?orderId=${this.orderInfo.orderId}`
})
},
// 格式化时间
formatTime(minutes) {
if (!minutes) return '';
const mins = parseInt(minutes);
if (mins < 60) {
return `${mins}分钟`;
} else {
const hours = Math.floor(mins / 60);
const remainingMins = mins % 60;
return remainingMins > 0 ? `${hours}小时${remainingMins}分钟` : `${hours}小时`;
}
}
}
}
@@ -659,6 +820,16 @@
}
}
&.member {
background: rgba(25, 118, 210, 0.08);
.badge-text {
font-size: 22rpx;
color: #1976D2;
font-weight: 500;
}
}
&.deposit {
background: #f5f5f5;