466 lines
11 KiB
Vue
466 lines
11 KiB
Vue
<template>
|
||
<view class="verify-page">
|
||
<!-- 扫码区域 -->
|
||
<view class="scan-section">
|
||
<view class="scan-icon" @click="scanCode">
|
||
<text class="icon">📷</text>
|
||
<text class="text">点击扫码核销</text>
|
||
</view>
|
||
<text class="scan-tip">扫描订单二维码或核销码进行核销</text>
|
||
</view>
|
||
|
||
<!-- 手动输入核销码 -->
|
||
<view class="input-section">
|
||
<view class="section-title">或手动输入核销码</view>
|
||
<view class="input-row">
|
||
<input
|
||
class="verify-input"
|
||
v-model="verifyCode"
|
||
placeholder="请输入12位核销码"
|
||
maxlength="12"
|
||
type="number"
|
||
/>
|
||
<button class="verify-btn" @click="manualVerify" :disabled="!verifyCode">
|
||
核销
|
||
</button>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 今日核销记录 -->
|
||
<view class="records-section">
|
||
<view class="section-title">今日核销记录</view>
|
||
<view class="record-list">
|
||
<view
|
||
v-for="(record, index) in records"
|
||
:key="index"
|
||
class="record-item"
|
||
@click="viewOrderDetail(record)"
|
||
>
|
||
<view class="record-header">
|
||
<text class="order-no">订单号: {{ record.orderNo }}</text>
|
||
<text class="verify-time">{{ formatTime(record.verifyTime) }}</text>
|
||
</view>
|
||
<view class="record-content">
|
||
<text class="user-name">用户: {{ record.userName || '未知' }}</text>
|
||
<text class="service-name">服务: {{ record.serviceName || '未知' }}</text>
|
||
</view>
|
||
<view class="record-footer">
|
||
<text class="status success">✓ 已核销</text>
|
||
<text class="amount">¥{{ record.totalAmount }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view v-if="records.length === 0" class="empty-tip">
|
||
<text class="empty-icon">📋</text>
|
||
<text class="empty-text">今日暂无核销记录</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import request from '@/utils/request'
|
||
|
||
export default {
|
||
data() {
|
||
return {
|
||
verifyCode: '',
|
||
records: []
|
||
}
|
||
},
|
||
|
||
onLoad() {
|
||
this.loadTodayRecords()
|
||
},
|
||
|
||
onShow() {
|
||
// 每次显示页面时刷新记录
|
||
this.loadTodayRecords()
|
||
},
|
||
|
||
methods: {
|
||
// 扫码核销
|
||
scanCode() {
|
||
uni.scanCode({
|
||
success: (res) => {
|
||
console.log('扫码结果:', res)
|
||
const code = res.result
|
||
|
||
// 判断扫描结果类型
|
||
if (code.includes('orderId=')) {
|
||
// 订单ID格式:orderId=123
|
||
const orderId = code.split('orderId=')[1]
|
||
this.scanVerifyByOrderId(orderId)
|
||
} else if (code.length === 12 && /^\d+$/.test(code)) {
|
||
// 12位数字核销码
|
||
this.verifyByCode(code)
|
||
} else {
|
||
// 尝试作为核销码处理
|
||
this.verifyByCode(code)
|
||
}
|
||
},
|
||
fail: (err) => {
|
||
console.error('扫码失败:', err)
|
||
uni.showToast({
|
||
title: '扫码失败',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
})
|
||
},
|
||
|
||
// 手动输入核销码核销
|
||
manualVerify() {
|
||
if (!this.verifyCode) {
|
||
uni.showToast({
|
||
title: '请输入核销码',
|
||
icon: 'none'
|
||
})
|
||
return
|
||
}
|
||
|
||
if (this.verifyCode.length !== 12) {
|
||
uni.showToast({
|
||
title: '核销码应为12位数字',
|
||
icon: 'none'
|
||
})
|
||
return
|
||
}
|
||
|
||
this.verifyByCode(this.verifyCode)
|
||
},
|
||
|
||
// 通过核销码核销
|
||
async verifyByCode(code) {
|
||
try {
|
||
uni.showLoading({ title: '核销中...' })
|
||
|
||
const res = await request.post('/api/order/verify', null, {
|
||
verifyCode: code
|
||
})
|
||
|
||
console.log('核销响应:', res)
|
||
|
||
uni.hideLoading()
|
||
|
||
uni.showModal({
|
||
title: '核销成功',
|
||
content: '订单已核销,服务已开始',
|
||
showCancel: false,
|
||
success: () => {
|
||
this.verifyCode = ''
|
||
this.loadTodayRecords()
|
||
}
|
||
})
|
||
} catch (e) {
|
||
console.error('核销失败:', e)
|
||
uni.hideLoading()
|
||
|
||
uni.showModal({
|
||
title: '核销失败',
|
||
content: e.message || e.msg || '核销码无效或订单状态不允许核销',
|
||
showCancel: false
|
||
})
|
||
}
|
||
},
|
||
|
||
// 通过订单ID核销
|
||
async scanVerifyByOrderId(orderId) {
|
||
try {
|
||
uni.showLoading({ title: '核销中...' })
|
||
|
||
const res = await request.post(`/api/order/scan-verify/${orderId}`)
|
||
|
||
console.log('核销响应:', res)
|
||
|
||
uni.hideLoading()
|
||
|
||
uni.showModal({
|
||
title: '核销成功',
|
||
content: '订单已核销,服务已开始',
|
||
showCancel: false,
|
||
success: () => {
|
||
this.loadTodayRecords()
|
||
}
|
||
})
|
||
} catch (e) {
|
||
console.error('核销失败:', e)
|
||
uni.hideLoading()
|
||
|
||
uni.showModal({
|
||
title: '核销失败',
|
||
content: e.message || e.msg || '订单不存在或状态不允许核销',
|
||
showCancel: false
|
||
})
|
||
}
|
||
},
|
||
|
||
// 加载今日核销记录
|
||
async loadTodayRecords() {
|
||
try {
|
||
const today = this.getToday()
|
||
|
||
console.log('=== 加载核销记录 ===')
|
||
console.log('今天日期:', today)
|
||
|
||
// 获取当前登录的教师ID
|
||
const teacherId = uni.getStorageSync('userId')
|
||
console.log('教师ID:', teacherId)
|
||
|
||
const res = await request.get('/api/order/list', {
|
||
teacherId: teacherId,
|
||
status: 2, // 2=服务中(已核销)
|
||
page: 1,
|
||
size: 50
|
||
})
|
||
|
||
console.log('核销记录返回:', res)
|
||
|
||
// 处理返回的数据格式
|
||
const data = res.data || res
|
||
|
||
if (data && data.records) {
|
||
console.log('所有订单记录:', data.records)
|
||
console.log('订单记录数量:', data.records.length)
|
||
|
||
// 筛选今天核销的订单
|
||
this.records = data.records.filter(order => {
|
||
console.log('检查订单:', order.id, 'verifyTime:', order.verifyTime)
|
||
|
||
if (!order.verifyTime) {
|
||
console.log('订单没有核销时间,跳过')
|
||
return false
|
||
}
|
||
|
||
// 处理不同的日期格式
|
||
let verifyDate = ''
|
||
if (typeof order.verifyTime === 'string') {
|
||
verifyDate = order.verifyTime.split(' ')[0]
|
||
} else {
|
||
console.log('verifyTime不是字符串:', typeof order.verifyTime)
|
||
return false
|
||
}
|
||
|
||
console.log('订单核销日期:', verifyDate, '今天:', today, '匹配:', verifyDate === today)
|
||
return verifyDate === today
|
||
})
|
||
console.log('今日核销记录数量:', this.records.length)
|
||
|
||
// 如果没有今日记录,显示提示
|
||
if (this.records.length === 0 && data.records.length > 0) {
|
||
console.log('⚠️ 有核销记录但不是今天的')
|
||
console.log('最近的核销时间:', data.records[0]?.verifyTime)
|
||
}
|
||
} else {
|
||
console.log('没有返回records数据')
|
||
this.records = []
|
||
}
|
||
} catch (e) {
|
||
console.error('加载核销记录失败:', e)
|
||
this.records = []
|
||
}
|
||
},
|
||
|
||
// 查看订单详情
|
||
viewOrderDetail(record) {
|
||
uni.navigateTo({
|
||
url: `/pages/order/detail?id=${record.id}`
|
||
})
|
||
},
|
||
|
||
// 获取今天日期
|
||
getToday() {
|
||
const now = new Date()
|
||
const year = now.getFullYear()
|
||
const month = String(now.getMonth() + 1).padStart(2, '0')
|
||
const day = String(now.getDate()).padStart(2, '0')
|
||
return `${year}-${month}-${day}`
|
||
},
|
||
|
||
// 格式化时间
|
||
formatTime(datetime) {
|
||
if (!datetime) return ''
|
||
const d = new Date(datetime)
|
||
const hours = String(d.getHours()).padStart(2, '0')
|
||
const minutes = String(d.getMinutes()).padStart(2, '0')
|
||
return `${hours}:${minutes}`
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.verify-page {
|
||
min-height: 100vh;
|
||
background: #f5f5f5;
|
||
padding: 30rpx;
|
||
}
|
||
|
||
.scan-section {
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
padding: 80rpx 40rpx;
|
||
border-radius: 20rpx;
|
||
text-align: center;
|
||
margin-bottom: 30rpx;
|
||
|
||
.scan-icon {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
cursor: pointer;
|
||
|
||
.icon {
|
||
font-size: 120rpx;
|
||
margin-bottom: 30rpx;
|
||
}
|
||
|
||
.text {
|
||
font-size: 36rpx;
|
||
color: #fff;
|
||
font-weight: bold;
|
||
}
|
||
}
|
||
|
||
.scan-tip {
|
||
display: block;
|
||
margin-top: 30rpx;
|
||
font-size: 26rpx;
|
||
color: rgba(255, 255, 255, 0.9);
|
||
}
|
||
}
|
||
|
||
.input-section {
|
||
background: #fff;
|
||
padding: 30rpx;
|
||
border-radius: 20rpx;
|
||
margin-bottom: 30rpx;
|
||
|
||
.section-title {
|
||
font-size: 28rpx;
|
||
color: #666;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.input-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 20rpx;
|
||
|
||
.verify-input {
|
||
flex: 1;
|
||
height: 80rpx;
|
||
padding: 0 20rpx;
|
||
background: #f5f5f5;
|
||
border-radius: 10rpx;
|
||
font-size: 32rpx;
|
||
}
|
||
|
||
.verify-btn {
|
||
width: 160rpx;
|
||
height: 80rpx;
|
||
line-height: 80rpx;
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
color: #fff;
|
||
border-radius: 10rpx;
|
||
font-size: 32rpx;
|
||
border: none;
|
||
padding: 0;
|
||
|
||
&[disabled] {
|
||
opacity: 0.5;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.records-section {
|
||
.section-title {
|
||
font-size: 32rpx;
|
||
font-weight: bold;
|
||
color: #333;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.record-list {
|
||
.record-item {
|
||
background: #fff;
|
||
padding: 30rpx;
|
||
border-radius: 16rpx;
|
||
margin-bottom: 20rpx;
|
||
|
||
.record-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 20rpx;
|
||
|
||
.order-no {
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.verify-time {
|
||
font-size: 24rpx;
|
||
color: #999;
|
||
}
|
||
}
|
||
|
||
.record-content {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 10rpx;
|
||
margin-bottom: 20rpx;
|
||
|
||
.user-name,
|
||
.service-name {
|
||
font-size: 26rpx;
|
||
color: #666;
|
||
}
|
||
}
|
||
|
||
.record-footer {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding-top: 20rpx;
|
||
border-top: 1rpx solid #f0f0f0;
|
||
|
||
.status {
|
||
font-size: 26rpx;
|
||
|
||
&.success {
|
||
color: #52c41a;
|
||
}
|
||
}
|
||
|
||
.amount {
|
||
font-size: 32rpx;
|
||
color: #ff6b00;
|
||
font-weight: bold;
|
||
}
|
||
}
|
||
}
|
||
|
||
.empty-tip {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
padding: 100rpx 0;
|
||
|
||
.empty-icon {
|
||
font-size: 100rpx;
|
||
margin-bottom: 20rpx;
|
||
opacity: 0.3;
|
||
}
|
||
|
||
.empty-text {
|
||
font-size: 28rpx;
|
||
color: #999;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</style>
|