第一个版本
This commit is contained in:
@@ -98,7 +98,7 @@
|
||||
<view class="code-modal">
|
||||
<text class="code-modal__title">入园码</text>
|
||||
<view class="qr-box">
|
||||
<image class="qr-img" src="/static/qr-demo.png" mode="aspectFit" />
|
||||
<image v-if="codeSrc" class="qr-img" :src="codeSrc" mode="aspectFit" />
|
||||
</view>
|
||||
<text class="code-modal__desc">入园时请出示此二维码+本人身份证</text>
|
||||
</view>
|
||||
@@ -162,7 +162,7 @@
|
||||
</view>
|
||||
<view class="sheet-row">
|
||||
<text class="sheet-label">预约状态</text>
|
||||
<text class="sheet-value">{{ statusLabel(detailRecord?.status || '') }}</text>
|
||||
<text class="sheet-value">{{ detailStatusLabel(detailRecord?.status || '') }}</text>
|
||||
</view>
|
||||
<view class="sheet-row">
|
||||
<text class="sheet-label">预约申请时间</text>
|
||||
@@ -177,12 +177,12 @@
|
||||
<text class="sheet-value">{{ detailRecord.cancelTime }}</text>
|
||||
</view>
|
||||
<!-- 核销人脸照片 -->
|
||||
<view v-if="detailRecord?.verifyPhoto && detailRecord?.status !== 'auto_used'" class="sheet-photo">
|
||||
<view v-if="detailRecord?.verifyPhoto" class="sheet-photo">
|
||||
<text class="sheet-label">核销照片</text>
|
||||
<image class="sheet-photo-avatar" :src="detailRecord.verifyPhoto" mode="aspectFill" @tap="previewPhoto(detailRecord.verifyPhoto)" />
|
||||
</view>
|
||||
<view v-if="detailRecord?.status !== 'used'" class="sheet-tip">
|
||||
<text class="sheet-tip__text">未提前取消且未入园将扣除1次年卡使用次数</text>
|
||||
<view v-if="detailRecord?.status !== 'used' && detailRecord?.cardInstruction" class="sheet-tip">
|
||||
<text class="sheet-tip__text">{{ detailRecord.cardInstruction }}</text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
@@ -191,9 +191,12 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { post } from '../../../../api/request'
|
||||
import { BASE_URL } from '../../../../config'
|
||||
|
||||
type RecordStatus = 'pending' | 'used' | 'cancelled' | 'auto_used'
|
||||
type RecordStatus = 'pending' | 'cancelled' | 'used'
|
||||
|
||||
interface ReserveRecord {
|
||||
id: string
|
||||
@@ -209,46 +212,95 @@ interface ReserveRecord {
|
||||
cancelTime?: string
|
||||
verifyTime?: string
|
||||
verifyPhoto?: string
|
||||
cardInstruction?: string
|
||||
cardModelId?: string
|
||||
}
|
||||
|
||||
const tabs = [
|
||||
{ label: '全部', value: '' },
|
||||
{ label: '待入园', value: 'pending' },
|
||||
{ label: '待使用', value: 'pending' },
|
||||
{ label: '已入园', value: 'used' },
|
||||
{ label: '已取消', value: 'cancelled' },
|
||||
{ label: '自动核销', value: 'auto_used' },
|
||||
]
|
||||
|
||||
const activeTab = ref('')
|
||||
const loading = ref(false)
|
||||
const refreshing = ref(false)
|
||||
const noMore = ref(false)
|
||||
const pageNo = ref(1)
|
||||
const pageSize = 10
|
||||
const codePopup = ref<any>(null)
|
||||
const cancelPopup = ref<any>(null)
|
||||
const detailPopup = ref<any>(null)
|
||||
const popupCode = ref('')
|
||||
const codeSrc = ref('')
|
||||
const detailRecord = ref<ReserveRecord | null>(null)
|
||||
const cancelRecord = ref<ReserveRecord | null>(null)
|
||||
|
||||
const records = ref<ReserveRecord[]>([
|
||||
{ id: 'YP26062601', scenicName: '武功山金顶景区', date: '2026-06-27', session: '08:00-10:00', status: 'pending', userName: '张三', maskedId: '360302****1234', cardType: '超级玩家卡', cardNo: 'CJ0000012398', applyTime: '2026-06-25 14:30' },
|
||||
{ id: 'YP26062502', scenicName: '杨岐山孽龙洞景区', date: '2026-06-26', session: '10:00-12:00', status: 'pending', userName: '张三', maskedId: '360302****1234', cardType: '赣湘全域旅游年卡', cardNo: 'CJ0000012399', applyTime: '2026-06-24 10:15' },
|
||||
{ id: 'YP26062003', scenicName: '仙凤三宝景区', date: '2026-06-20', session: '14:00-16:00', status: 'used', userName: '张三', maskedId: '360302****1234', cardType: '超级玩家卡', cardNo: 'CJ0000012400', applyTime: '2026-06-18 09:20', verifyTime: '2026-06-20 09:45', verifyPhoto: 'https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?fm=jpg&q=80&w=200&h=200&fit=crop' },
|
||||
{ id: 'YP26061804', scenicName: '武功山君澜温泉', date: '2026-06-18', status: 'cancelled', userName: '张三', maskedId: '360302****1234', cardType: '赣湘全域旅游年卡', cardNo: 'CJ0000012401', applyTime: '2026-06-16 16:40', cancelTime: '2026-06-17 20:30' },
|
||||
{ id: 'YP26061505', scenicName: '凯光新天地生态旅游区', date: '2026-06-15', status: 'auto_used', userName: '张三', maskedId: '360302****1234', cardType: '超级玩家卡', cardNo: 'CJ0000012402', applyTime: '2026-06-13 11:05', verifyTime: '2026-06-16 00:00', verifyPhoto: 'https://images.unsplash.com/photo-1527980965255-d3b416303d12?fm=jpg&q=80&w=200&h=200&fit=crop' },
|
||||
{ id: 'YP26061006', scenicName: '和平农场·哎马乐园', date: '2026-06-10', session: '08:00-12:00', status: 'used', userName: '张三', maskedId: '360302****1234', cardType: '赣湘全域旅游年卡', cardNo: 'CJ0000012403', applyTime: '2026-06-08 08:50', verifyTime: '2026-06-10 10:22', verifyPhoto: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?fm=jpg&q=80&w=200&h=200&fit=crop' },
|
||||
])
|
||||
const records = ref<ReserveRecord[]>([])
|
||||
|
||||
const filteredRecords = computed(() => {
|
||||
if (!activeTab.value) return records.value
|
||||
return records.value.filter(r => r.status === activeTab.value)
|
||||
})
|
||||
const filteredRecords = computed(() => records.value)
|
||||
|
||||
function statusLabel(status: string) {
|
||||
const map: Record<string, string> = { pending: '待入园', used: '已入园', cancelled: '已取消', auto_used: '自动核销' }
|
||||
const map: Record<string, string> = { pending: '待使用', cancelled: '已取消', used: '已入园' }
|
||||
return map[status] || status
|
||||
}
|
||||
|
||||
function detailStatusLabel(status: number | string) {
|
||||
const map: Record<string, string> = { '1': '预约成功', '2': '预约失败', '3': '已取消', '4': '已入园' }
|
||||
return map[String(status)] || String(status)
|
||||
}
|
||||
|
||||
async function fetchRecords(append = false) {
|
||||
if (loading.value) return
|
||||
loading.value = true
|
||||
try {
|
||||
const phone = (uni.getStorageSync('phone') as string) || ''
|
||||
const res = await post<any>('/mpReserve/queryUsageRecordList', {
|
||||
phone,
|
||||
status: activeTab.value,
|
||||
pageNo: pageNo.value,
|
||||
pageSize,
|
||||
})
|
||||
const list = (res.list || res.records || []).map((item: any) => ({
|
||||
id: item.id || '',
|
||||
scenicName: item.scenicName || item.resourceName || '',
|
||||
date: item.appTime || item.date || '',
|
||||
session: item.sessionTime || item.session || '',
|
||||
status: item.status || 'pending',
|
||||
userName: item.name || item.userName || '',
|
||||
maskedId: item.identityNo ? `${item.identityNo.slice(0, 6)}****${item.identityNo.slice(-4)}` : '',
|
||||
cardType: item.cardModelName || item.cardType || '',
|
||||
cardNo: item.cardNum || item.cardNo || '',
|
||||
applyTime: item.createTime || item.applyTime || '',
|
||||
cancelTime: item.cancelTime || '',
|
||||
verifyTime: item.verifyTime || '',
|
||||
verifyPhoto: item.verifyPhoto || '',
|
||||
cardInstruction: item.cardInstruction || '',
|
||||
cardModelId: item.cardModelId || item.thirdId || '',
|
||||
}))
|
||||
if (append) {
|
||||
records.value.push(...list)
|
||||
} else {
|
||||
records.value = list
|
||||
}
|
||||
noMore.value = list.length < pageSize
|
||||
} catch (_e) {
|
||||
records.value = []
|
||||
noMore.value = true
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onLoad(() => {
|
||||
fetchRecords()
|
||||
})
|
||||
|
||||
watch(activeTab, () => {
|
||||
pageNo.value = 1
|
||||
fetchRecords()
|
||||
})
|
||||
|
||||
function canCancel(date: string) {
|
||||
const today = new Date()
|
||||
today.setHours(0, 0, 0, 0)
|
||||
@@ -261,40 +313,105 @@ function showCancelModal(r: ReserveRecord) {
|
||||
cancelPopup.value?.open()
|
||||
}
|
||||
|
||||
function confirmCancel() {
|
||||
async function confirmCancel() {
|
||||
cancelPopup.value?.close()
|
||||
if (cancelRecord.value) {
|
||||
const idx = records.value.findIndex(o => o.id === cancelRecord.value!.id)
|
||||
if (idx !== -1) {
|
||||
records.value[idx].status = 'cancelled'
|
||||
records.value[idx].cancelTime = new Date().toISOString().slice(0, 16).replace('T', ' ')
|
||||
try {
|
||||
await post<any>(`${BASE_URL}/zhy-api/cardBag/cancelAppoint`, {
|
||||
appointId: cancelRecord.value.id,
|
||||
channel: 'Channel_SZH',
|
||||
})
|
||||
const idx = records.value.findIndex(o => o.id === cancelRecord.value!.id)
|
||||
if (idx !== -1) {
|
||||
records.value[idx].status = 'cancelled'
|
||||
records.value[idx].cancelTime = new Date().toISOString().slice(0, 16).replace('T', ' ')
|
||||
}
|
||||
uni.showToast({ title: '已取消预约', icon: 'success' })
|
||||
} catch (e: any) {
|
||||
uni.showToast({ title: e?.message || '取消失败', icon: 'none' })
|
||||
}
|
||||
uni.showToast({ title: '已取消预约', icon: 'success' })
|
||||
}
|
||||
}
|
||||
|
||||
function showCode(r: ReserveRecord) {
|
||||
popupCode.value = r.id
|
||||
codePopup.value?.open()
|
||||
async function showCode(r: ReserveRecord) {
|
||||
codeSrc.value = ''
|
||||
try {
|
||||
const res = await post<any>(`${BASE_URL}/zhy-api/cardBag/getQrByAppointmentId?appointmentId=${r.id}`)
|
||||
if (res.qrType == 1) {
|
||||
codeSrc.value = 'data:image/png;base64,' + res.qrCode
|
||||
codePopup.value?.open()
|
||||
} else if (res.qrType == 2) {
|
||||
codeSrc.value = res.qrUrl
|
||||
codePopup.value?.open()
|
||||
} else {
|
||||
await fetchQrByCard(r)
|
||||
}
|
||||
} catch (_e) {
|
||||
await fetchQrByCard(r)
|
||||
}
|
||||
}
|
||||
|
||||
function viewDetail(r: ReserveRecord) {
|
||||
detailRecord.value = r
|
||||
async function fetchQrByCard(r: ReserveRecord) {
|
||||
try {
|
||||
const phone = (uni.getStorageSync('phone') as string) || ''
|
||||
const res = await post<any>(`${BASE_URL}/zhy-api/cardBag/qrAndUserInfo`, {
|
||||
cardNum: r.cardNo,
|
||||
channel: 'Channel_SZH',
|
||||
mobile: phone,
|
||||
scenicId: '',
|
||||
thirdId: r.cardModelId,
|
||||
})
|
||||
if (res.qrCode) {
|
||||
codeSrc.value = res.qrCode.startsWith('data:') ? res.qrCode : 'data:image/png;base64,' + res.qrCode
|
||||
codePopup.value?.open()
|
||||
} else {
|
||||
uni.showToast({ icon: 'none', title: '暂无核销码' })
|
||||
}
|
||||
} catch (_e) {
|
||||
uni.showToast({ icon: 'none', title: '获取入园码失败' })
|
||||
}
|
||||
}
|
||||
|
||||
async function viewDetail(r: ReserveRecord) {
|
||||
try {
|
||||
const phone = (uni.getStorageSync('phone') as string) || ''
|
||||
const res = await post<any>('/mpReserve/getAppointmentDetail', { phone, appointId: r.id })
|
||||
if (res) {
|
||||
detailRecord.value = {
|
||||
id: res.id || r.id,
|
||||
scenicName: res.resourceName || r.scenicName,
|
||||
date: res.appTime || r.date,
|
||||
session: res.sessionTime || r.session,
|
||||
status: res.status || r.status || 'pending',
|
||||
userName: res.name || r.userName,
|
||||
maskedId: res.identityNo ? `${res.identityNo.slice(0, 6)}****${res.identityNo.slice(-4)}` : r.maskedId,
|
||||
cardType: res.cardModelName || r.cardType,
|
||||
cardNo: res.cardNum || r.cardNo,
|
||||
applyTime: res.createTime || r.applyTime,
|
||||
cancelTime: res.cancelTime || r.cancelTime,
|
||||
verifyTime: res.verifyTime || r.verifyTime,
|
||||
verifyPhoto: res.verifyPhoto || r.verifyPhoto,
|
||||
cardInstruction: res.cardInstruction || r.cardInstruction || '',
|
||||
cardModelId: res.cardModelId || r.cardModelId || '',
|
||||
}
|
||||
}
|
||||
} catch (_e) {
|
||||
detailRecord.value = r
|
||||
}
|
||||
detailPopup.value?.open()
|
||||
}
|
||||
|
||||
function onRefresh() {
|
||||
async function onRefresh() {
|
||||
refreshing.value = true
|
||||
setTimeout(() => { refreshing.value = false }, 800)
|
||||
pageNo.value = 1
|
||||
await fetchRecords()
|
||||
refreshing.value = false
|
||||
}
|
||||
|
||||
function onLoadMore() {
|
||||
async function onLoadMore() {
|
||||
if (loading.value || noMore.value) return
|
||||
loading.value = true
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
noMore.value = true
|
||||
}, 800)
|
||||
pageNo.value++
|
||||
await fetchRecords(true)
|
||||
}
|
||||
|
||||
function goReserve() {
|
||||
@@ -392,9 +509,8 @@ function previewPhoto(url: string) {
|
||||
border-radius: 8rpx;
|
||||
|
||||
&--pending { background: #e8f3ff; }
|
||||
&--used { background: #f0fff4; }
|
||||
&--cancelled { background: $uv-bg-color; }
|
||||
&--auto_used { background: #fff8e8; }
|
||||
&--used { background: #f0fff4; }
|
||||
}
|
||||
|
||||
.status-tag__text {
|
||||
@@ -404,9 +520,8 @@ function previewPhoto(url: string) {
|
||||
}
|
||||
|
||||
.status-tag--pending .status-tag__text { color: $uv-primary; }
|
||||
.status-tag--used .status-tag__text { color: $color-status-success; }
|
||||
.status-tag--cancelled .status-tag__text { color: $color-status-cancel; }
|
||||
.status-tag--auto_used .status-tag__text { color: $color-status-pending; }
|
||||
.status-tag--used .status-tag__text { color: $color-status-success; }
|
||||
|
||||
.record-card-info {
|
||||
display: flex;
|
||||
@@ -511,7 +626,7 @@ function previewPhoto(url: string) {
|
||||
|
||||
.qr-img { width: 300rpx; height: 300rpx; }
|
||||
|
||||
.code-modal__desc { font-family: $font-family-body; font-size: $font-size-sm; color: $uv-tips-color; margin-top: 24rpx; }
|
||||
.code-modal__desc { font-family: $font-family-body; font-size: $font-size-sm; color: $uv-tips-color; }
|
||||
|
||||
/* ===== Cancel Modal ===== */
|
||||
.cancel-modal {
|
||||
|
||||
Reference in New Issue
Block a user