feat: 我的订单页面等调整
This commit is contained in:
@@ -1,54 +1,170 @@
|
||||
<template>
|
||||
<view class="flex h-full min-w-0 flex-col overflow-hidden bg-[#f8fafc]">
|
||||
<view class="flex h-full min-w-0 flex-col overflow-hidden bg-slate-100">
|
||||
<scroll-view scroll-y class="min-h-0 min-w-0 flex-1">
|
||||
<view class="box-border w-full min-w-0 px-[16px] py-[16px]">
|
||||
<view class="box-border w-full min-w-0 overflow-hidden rounded-[16px] bg-white p-[16px]">
|
||||
<text class="block break-words text-[16px] font-medium text-[#171717]">{{ order.productName || '景区产品' }}</text>
|
||||
<view class="mt-[10px] flex min-w-0 text-[13px] text-[#667085]"><text class="shrink-0">订单号</text><text class="ml-[8px] min-w-0 flex-1 break-all text-right">{{ order.orderNo || order.id || '-' }}</text></view>
|
||||
<text class="mt-[8px] block break-words text-[14px] text-[#475467]">{{ order.visitDate || '-' }} {{ order.timeSlot || '全天' }}</text>
|
||||
<text class="mt-[6px] block break-words text-[14px] text-[#475467]">出行人 {{ firstTraveler }}</text>
|
||||
</view>
|
||||
|
||||
<view v-if="order.gateCredential" class="mt-[12px] box-border w-full min-w-0 overflow-hidden rounded-[18px] bg-[#5b21b6] p-[18px]">
|
||||
<text class="text-[18px] font-semibold text-white">第三方票务凭证</text>
|
||||
<text class="mt-[6px] block text-[13px] text-[#ddd6fe]">请向检票人员出示此二维码</text>
|
||||
<view class="mt-[16px] box-border flex w-full min-w-0 flex-col items-center rounded-[12px] bg-white p-[14px]">
|
||||
<view class="grid h-[176px] w-[176px] max-w-full grid-cols-9 grid-rows-9 border border-[#5b21b6] p-[4px]"><view v-for="(cell, index) in gateGrid" :key="index" class="h-full w-full" :class="cell ? 'bg-[#5b21b6]' : 'bg-white'" /></view>
|
||||
<view class="mt-[12px] flex min-w-0 items-center justify-center"><text class="text-[13px] text-[#667085]">核验状态</text><text class="ml-[8px] text-[16px] font-medium text-[#15803d]">{{ order.gateCredential.status === 'issued' ? '可使用' : '处理中' }}</text></view>
|
||||
<view class="box-border flex w-full min-w-0 flex-col gap-4 p-4 pb-6">
|
||||
<view class="box-border w-full min-w-0 rounded-2xl bg-white p-4 shadow-sm">
|
||||
<view class="flex min-w-0 items-start justify-between gap-3">
|
||||
<view class="min-w-0 flex-1">
|
||||
<text class="block truncate font-bold text-slate-900">{{ order.productName || '景区产品' }}</text>
|
||||
<text class="mt-1 block text-xs text-slate-400">{{ order.visitDate || '—' }} · {{ order.timeSlot || '全天' }}</text>
|
||||
</view>
|
||||
<text class="shrink-0 rounded-full border px-2.5 py-1 text-xs font-semibold" :class="orderStatus.className">{{ orderStatus.label }}</text>
|
||||
</view>
|
||||
<view class="mt-[16px] min-w-0 border-t border-[#7c3aed] pt-[12px]"><text v-for="right in gateRights" :key="right.id" class="mb-[6px] block break-words text-[13px] text-[#ede9fe]">{{ right.name }} × {{ right.quantity || 0 }}</text><text class="mt-[6px] block text-[12px] leading-[18px] text-[#ddd6fe]">请在预约日期和时段内完成核验,截图可能无法正常使用。</text></view>
|
||||
</view>
|
||||
|
||||
<view v-if="order.selfCredential" class="mt-[12px] box-border w-full min-w-0 overflow-hidden rounded-[18px] border border-[#bfdbfe] bg-[#eff6ff] p-[18px]">
|
||||
<text class="text-[18px] font-semibold text-[#1e3a8a]">自营园内权益</text>
|
||||
<text class="mt-[6px] block text-[13px] text-[#3b82f6]">请向服务人员出示此二维码</text>
|
||||
<view class="mt-[16px] box-border flex w-full min-w-0 flex-col items-center rounded-[12px] bg-white p-[14px]">
|
||||
<view class="grid h-[176px] w-[176px] max-w-full grid-cols-9 grid-rows-9 border border-[#1d4ed8] p-[4px]"><view v-for="(cell, index) in selfGrid" :key="index" class="h-full w-full" :class="cell ? 'bg-[#1d4ed8]' : 'bg-white'" /></view>
|
||||
<view class="mt-[12px] flex min-w-0 items-center justify-center"><text class="text-[13px] text-[#667085]">核验状态</text><text class="ml-[8px] text-[16px] font-medium text-[#15803d]">{{ order.selfCredential.status === 'issued' ? '可使用' : '处理中' }}</text></view>
|
||||
<view class="mt-3 flex min-w-0 justify-between gap-3 border-t border-slate-100 pt-3 text-sm">
|
||||
<text class="shrink-0 text-slate-500">游客</text>
|
||||
<text class="min-w-0 break-words text-right font-semibold text-slate-800">{{ firstTraveler }} · {{ maskPhone(order.phone) }}</text>
|
||||
</view>
|
||||
<view class="mt-[16px] min-w-0 border-t border-[#bfdbfe] pt-[12px]"><text v-for="right in selfRights" :key="right.id" class="mb-[6px] block break-words text-[13px] text-[#1e3a8a]">{{ right.name }} × {{ right.quantity || 0 }}</text><text class="mt-[6px] block text-[12px] leading-[18px] text-[#3b82f6]">请在服务点出示二维码,权益核销后不可再次使用。</text></view>
|
||||
</view>
|
||||
|
||||
<view v-if="!order.gateCredential && !order.selfCredential" class="flex min-h-[260px] min-w-0 flex-col items-center justify-center"><uni-icons type="info" size="34" color="#98a2b3" /><text class="mt-[12px] text-[14px] text-[#98a2b3]">暂无可用凭证</text></view>
|
||||
<view class="h-[24px]" />
|
||||
<view v-if="order.gateCredential" class="box-border w-full min-w-0 rounded-2xl bg-white p-5 shadow-sm">
|
||||
<view class="flex min-w-0 items-start justify-between gap-3">
|
||||
<view class="flex min-w-0 flex-1 items-center gap-3">
|
||||
<view class="flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-blue-50">
|
||||
<uni-icons type="scan" size="21" color="#2563eb" />
|
||||
</view>
|
||||
<view class="min-w-0 flex-1">
|
||||
<text class="block text-xs text-blue-500">第三方票务系统出票</text>
|
||||
<text class="mt-1 block font-bold text-slate-900">第三方票务凭证</text>
|
||||
</view>
|
||||
</view>
|
||||
<text class="shrink-0 rounded-full bg-blue-50 px-2.5 py-1 text-xs font-semibold text-blue-700">{{ gateStatusText }}</text>
|
||||
</view>
|
||||
|
||||
<view class="mt-4 box-border flex w-full min-w-0 justify-center rounded-2xl bg-slate-50 p-4">
|
||||
<Qrcode
|
||||
cid="ticket-gate-credential"
|
||||
:size="164"
|
||||
unit="px"
|
||||
:val="gateQrValue"
|
||||
:load-make="true"
|
||||
:onval="true"
|
||||
:show-loading="false"
|
||||
foreground="#0f172a"
|
||||
pdground="#0f172a"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view class="mt-4 flex flex-col gap-2">
|
||||
<view v-for="right in gateRights" :key="right.id" class="flex min-w-0 items-center justify-between gap-3 rounded-xl bg-slate-50 px-3 py-2.5">
|
||||
<text class="min-w-0 flex-1 break-words text-sm font-medium text-slate-700">{{ right.name }} × {{ right.quantity || 0 }}</text>
|
||||
<text class="shrink-0 rounded-full px-2.5 py-1 text-xs font-semibold" :class="entitlementMeta(right.status).className">{{ entitlementMeta(right.status).label }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<text class="mt-3 block text-center text-xs leading-5 text-slate-400">门票可凭此码或身份证入园;观光车按第三方票务规则使用</text>
|
||||
</view>
|
||||
|
||||
<view v-if="order.selfCredential" class="box-border w-full min-w-0 rounded-2xl bg-white p-5 shadow-sm">
|
||||
<view class="flex min-w-0 items-start justify-between gap-3">
|
||||
<view class="flex min-w-0 flex-1 items-center gap-3">
|
||||
<view class="flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-emerald-50">
|
||||
<uni-icons type="checkbox-filled" size="21" color="#059669" />
|
||||
</view>
|
||||
<view class="min-w-0 flex-1">
|
||||
<text class="block text-xs text-emerald-600">园内工作人员扫码</text>
|
||||
<text class="mt-1 block font-bold text-slate-900">园内权益凭证</text>
|
||||
</view>
|
||||
</view>
|
||||
<text class="shrink-0 rounded-full bg-emerald-50 px-2.5 py-1 text-xs font-semibold text-emerald-700">自营凭证</text>
|
||||
</view>
|
||||
|
||||
<view class="mt-4 box-border flex w-full min-w-0 justify-center rounded-2xl bg-slate-50 p-4">
|
||||
<Qrcode
|
||||
cid="ticket-self-credential"
|
||||
:size="164"
|
||||
unit="px"
|
||||
:val="selfQrValue"
|
||||
:load-make="true"
|
||||
:onval="true"
|
||||
:show-loading="false"
|
||||
foreground="#0f172a"
|
||||
pdground="#0f172a"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view class="mt-4 flex flex-col gap-2">
|
||||
<view v-for="right in selfRights" :key="right.id" class="flex min-w-0 items-center justify-between gap-3 rounded-xl bg-slate-50 px-3 py-2.5">
|
||||
<text class="min-w-0 flex-1 break-words text-sm font-medium text-slate-700">{{ right.name }} × {{ right.quantity || 0 }}{{ right.unit || '份' }}</text>
|
||||
<text class="shrink-0 rounded-full px-2.5 py-1 text-xs font-semibold" :class="entitlementMeta(right.status).className">{{ entitlementMeta(right.status).label }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<text class="mt-3 block text-center text-xs leading-5 text-slate-400">多个自营二销项目共用此码,员工扫码后选择项目核销</text>
|
||||
</view>
|
||||
|
||||
<view v-if="!order.gateCredential && !order.selfCredential" class="box-border flex min-h-[300px] w-full min-w-0 flex-col items-center justify-center rounded-2xl bg-white p-6 shadow-sm">
|
||||
<view class="flex h-16 w-16 items-center justify-center rounded-full bg-slate-100">
|
||||
<uni-icons type="info" size="30" color="#94a3b8" />
|
||||
</view>
|
||||
<text class="mt-4 text-sm font-semibold text-slate-500">暂无可用凭证</text>
|
||||
<text class="mt-2 text-center text-xs leading-5 text-slate-400">出票完成后,二维码会展示在这里</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="box-border w-full min-w-0 bg-white px-[16px] py-[12px] pb-[calc(env(safe-area-inset-bottom)+12px)]"><button class="box-border w-full min-w-0 rounded-[24px] bg-[#7c3aed] p-0 text-[15px] leading-[48px] text-white" @click="emit('detail')">查看订单详情</button></view>
|
||||
|
||||
<view class="box-border w-full min-w-0 border-t border-slate-200 bg-white p-4 pb-[calc(env(safe-area-inset-bottom)+16px)]">
|
||||
<button class="detail-action m-0 box-border h-12 w-full min-w-0 overflow-hidden rounded-full border border-slate-200 bg-white p-0 text-sm font-semibold leading-[48px] text-slate-600" @tap="emit('detail')">查看订单详情</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { computed } from "vue";
|
||||
import Qrcode from "@/components/Qrcode/index.vue";
|
||||
import { ENTITLEMENT_STATUS_META, ORDER_STATUS_META } from "../data.js";
|
||||
|
||||
const props = defineProps({ order: { type: Object, required: true } })
|
||||
const emit = defineEmits(['detail'])
|
||||
const firstTraveler = computed(() => props.order?.travelers?.[0]?.name || '未填写出行人')
|
||||
const gateRights = computed(() => (props.order?.entitlements || []).filter((right) => right.fulfillment === 'third_party'))
|
||||
const selfRights = computed(() => (props.order?.entitlements || []).filter((right) => right.fulfillment === 'self'))
|
||||
const makeGrid = (token) => {
|
||||
const source = String(token || 'credential')
|
||||
return Array.from({ length: 81 }, (_, index) => ((source.charCodeAt(index % source.length) + index * 17 + Math.floor(index / 9) * 7) % 5) < 3)
|
||||
}
|
||||
const gateGrid = computed(() => makeGrid(props.order?.gateCredential?.qrToken))
|
||||
const selfGrid = computed(() => makeGrid(props.order?.selfCredential?.qrToken))
|
||||
const props = defineProps({ order: { type: Object, required: true } });
|
||||
const emit = defineEmits(["detail"]);
|
||||
const fallbackOrderStatus = {
|
||||
label: "待使用",
|
||||
className: "border-blue-200 bg-blue-50 text-blue-700",
|
||||
};
|
||||
const fallbackEntitlementStatus = {
|
||||
label: "待使用",
|
||||
className: "bg-blue-50 text-blue-700",
|
||||
};
|
||||
|
||||
const orderStatus = computed(
|
||||
() => ORDER_STATUS_META[props.order?.status] || fallbackOrderStatus
|
||||
);
|
||||
const firstTraveler = computed(
|
||||
() =>
|
||||
props.order?.customerName ||
|
||||
props.order?.travelers?.[0]?.name ||
|
||||
"未填写出行人"
|
||||
);
|
||||
const gateRights = computed(() =>
|
||||
(props.order?.entitlements || []).filter(
|
||||
(right) => right.fulfillment === "third_party"
|
||||
)
|
||||
);
|
||||
const selfRights = computed(() =>
|
||||
(props.order?.entitlements || []).filter(
|
||||
(right) => right.fulfillment === "self"
|
||||
)
|
||||
);
|
||||
const resolveGateQrValue = (order) =>
|
||||
String(order?.gateCredential?.qrToken || order?.gateCredential?.ticketNo || order?.id || "ticket-gate-demo");
|
||||
const resolveSelfQrValue = (order) =>
|
||||
String(order?.selfCredential?.qrToken || order?.selfCredential?.credentialNo || order?.id || "ticket-self-demo");
|
||||
const gateQrValue = computed(() => resolveGateQrValue(props.order));
|
||||
const selfQrValue = computed(() => resolveSelfQrValue(props.order));
|
||||
const gateStatusText = computed(
|
||||
() =>
|
||||
({
|
||||
issued: "可使用",
|
||||
used: "已使用",
|
||||
issuing: "出票中",
|
||||
failed: "出票失败",
|
||||
})[props.order?.gateCredential?.status] || "可使用"
|
||||
);
|
||||
const entitlementMeta = (status) =>
|
||||
ENTITLEMENT_STATUS_META[status] || fallbackEntitlementStatus;
|
||||
const maskPhone = (value) =>
|
||||
String(value || "—").replace(/^(\d{3})\d{4}(\d{4})$/, "$1****$2");
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.detail-action::after {
|
||||
border: 0;
|
||||
border-radius: 9999px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,77 +1,159 @@
|
||||
<template>
|
||||
<view class="flex h-full min-w-0 flex-col overflow-hidden bg-[#f8fafc]">
|
||||
<view class="relative flex h-full min-w-0 flex-col overflow-hidden bg-slate-100">
|
||||
<scroll-view scroll-y class="min-h-0 min-w-0 flex-1">
|
||||
<view class="box-border w-full min-w-0 px-[16px] py-[16px]">
|
||||
<view class="box-border w-full min-w-0 overflow-hidden rounded-[16px] bg-white p-[16px]">
|
||||
<view class="flex min-w-0 items-start justify-between gap-[10px]">
|
||||
<text class="min-w-0 flex-1 break-words text-[16px] font-medium leading-[22px] text-[#171717]">{{ order.productName || '景区产品' }}</text>
|
||||
<text class="shrink-0 rounded-[12px] bg-[#ecfdf3] px-[8px] py-[3px] text-[12px] text-[#15803d]">{{ statusText }}</text>
|
||||
<view class="box-border flex w-full min-w-0 flex-col gap-3 p-4 pb-28">
|
||||
<view class="box-border w-full min-w-0 rounded-2xl bg-white p-4">
|
||||
<view class="flex min-w-0 items-start justify-between gap-3">
|
||||
<text class="min-w-0 flex-1 break-words text-base font-bold text-slate-900">{{ order.productName }}</text>
|
||||
<text class="inline-flex shrink-0 items-center rounded-full border px-2.5 py-1 text-xs font-semibold" :class="statusMeta.className">{{ statusMeta.label }}</text>
|
||||
</view>
|
||||
<view class="mt-[12px] flex min-w-0 items-start text-[13px] text-[#667085]">
|
||||
<text class="shrink-0">订单号</text>
|
||||
<text class="ml-[8px] min-w-0 flex-1 break-all text-right">{{ order.orderNo || order.id || '-' }}</text>
|
||||
<view class="mt-4 flex min-w-0 items-center justify-between gap-3 text-sm">
|
||||
<text class="shrink-0 text-slate-400">订单号</text>
|
||||
<text class="min-w-0 break-all text-right text-slate-500">{{ order.orderNo || order.id || '—' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="mt-[12px] box-border w-full min-w-0 rounded-[16px] bg-white p-[16px]">
|
||||
<text class="text-[16px] font-medium text-[#171717]">使用信息</text>
|
||||
<view class="mt-[14px] flex min-w-0 justify-between gap-[12px] text-[14px]"><text class="shrink-0 text-[#667085]">使用日期</text><text class="min-w-0 break-words text-right text-[#344054]">{{ order.visitDate || '-' }}</text></view>
|
||||
<view class="mt-[12px] flex min-w-0 justify-between gap-[12px] text-[14px]"><text class="shrink-0 text-[#667085]">使用时段</text><text class="min-w-0 break-words text-right text-[#344054]">{{ order.timeSlot || '全天' }}</text></view>
|
||||
</view>
|
||||
|
||||
<view class="mt-[12px] box-border w-full min-w-0 rounded-[16px] bg-white p-[16px]">
|
||||
<text class="text-[16px] font-medium text-[#171717]">出行人</text>
|
||||
<view v-for="traveler in order.travelers || []" :key="traveler.id || traveler.name" class="mt-[14px] flex min-w-0 justify-between gap-[12px] text-[14px]">
|
||||
<text class="min-w-0 flex-1 break-words text-[#344054]">{{ traveler.name || '-' }}</text>
|
||||
<text class="shrink-0 text-[#667085]">{{ traveler.idType || '游客' }}</text>
|
||||
</view>
|
||||
<view v-if="!(order.travelers || []).length" class="mt-[14px] text-[14px] text-[#98a2b3]">暂无出行人信息</view>
|
||||
<view class="mt-[14px] flex min-w-0 border-t border-[#f2f4f7] pt-[14px] text-[14px]">
|
||||
<text class="shrink-0 text-[#667085]">联系人手机</text>
|
||||
<text class="ml-[12px] min-w-0 flex-1 break-all text-right text-[#344054]">{{ order.phone || '-' }}</text>
|
||||
<view class="box-border w-full min-w-0 rounded-2xl bg-white p-4">
|
||||
<text class="block text-base font-bold text-slate-900">使用信息</text>
|
||||
<view class="mt-4 flex flex-col gap-3 text-sm">
|
||||
<view class="flex min-w-0 justify-between gap-3">
|
||||
<text class="shrink-0 text-slate-400">使用日期</text>
|
||||
<text class="min-w-0 break-words text-right font-semibold text-slate-900">{{ order.visitDate }}</text>
|
||||
</view>
|
||||
<view class="flex min-w-0 justify-between gap-3">
|
||||
<text class="shrink-0 text-slate-400">使用时段</text>
|
||||
<text class="min-w-0 break-words text-right font-semibold text-slate-900">{{ order.timeSlot }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="mt-[12px] box-border w-full min-w-0 rounded-[16px] bg-white p-[16px]">
|
||||
<text class="text-[16px] font-medium text-[#171717]">权益与使用状态</text>
|
||||
<view v-for="right in order.entitlements || []" :key="right.id" class="mt-[14px] flex min-w-0 items-center justify-between gap-[12px] border-b border-[#f2f4f7] pb-[12px] last:border-b-0 last:pb-0">
|
||||
<view class="min-w-0 flex-1"><text class="block break-words text-[14px] text-[#344054]">{{ right.name }}</text><text class="mt-[4px] block text-[12px] text-[#98a2b3]">{{ fulfillmentText(right.fulfillment) }}</text></view>
|
||||
<text class="shrink-0 text-right text-[13px] text-[#667085]">{{ right.quantity || 0 }}份 · {{ rightStatus(right.status) }}</text>
|
||||
<view class="box-border w-full min-w-0 rounded-2xl bg-white p-4">
|
||||
<text class="block text-base font-bold text-slate-900">出行人</text>
|
||||
<view class="mt-4 flex min-w-0 justify-between gap-3 text-sm">
|
||||
<text class="min-w-0 flex-1 break-words text-slate-500">{{ order.customerName || order.contactName || order.travelers?.[0]?.name || '—' }}</text>
|
||||
<text class="shrink-0 text-slate-400">{{ travelerIdType }}</text>
|
||||
</view>
|
||||
<view class="mt-4 flex min-w-0 justify-between gap-3 border-t border-slate-100 pt-4 text-sm">
|
||||
<text class="shrink-0 text-slate-400">联系手机</text>
|
||||
<text class="min-w-0 break-all text-right font-semibold text-slate-700">{{ order.phone || '—' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="mt-[12px] box-border w-full min-w-0 rounded-[16px] bg-white p-[16px]">
|
||||
<text class="text-[16px] font-medium text-[#171717]">费用信息</text>
|
||||
<view class="mt-[14px] flex justify-between gap-[12px] text-[14px]"><text class="text-[#667085]">产品金额</text><text class="text-right text-[#344054]">¥{{ money(order.productAmount) }}</text></view>
|
||||
<view class="mt-[12px] flex justify-between gap-[12px] text-[14px]"><text class="text-[#667085]">保险金额</text><text class="text-right text-[#344054]">¥{{ money(order.insuranceAmount) }}</text></view>
|
||||
<view class="mt-[12px] flex justify-between gap-[12px] border-t border-[#f2f4f7] pt-[12px] text-[14px]"><text class="text-[#667085]">实付金额</text><text class="font-semibold text-[#ef4444]">¥{{ money(order.paidAmount) }}</text></view>
|
||||
<view class="mt-[12px] flex min-w-0 justify-between gap-[12px] text-[13px]"><text class="shrink-0 text-[#667085]">支付时间</text><text class="min-w-0 break-words text-right text-[#344054]">{{ order.paidAt || '-' }}</text></view>
|
||||
<view class="box-border w-full min-w-0 rounded-2xl bg-white p-4">
|
||||
<text class="font-bold text-slate-900">权益与使用状态</text>
|
||||
<view class="mt-3 divide-y divide-slate-100">
|
||||
<view v-for="right in order.entitlements || []" :key="right.id" class="flex min-w-0 items-center justify-between gap-3 py-3">
|
||||
<view class="min-w-0 flex-1">
|
||||
<text class="block break-words text-sm font-semibold text-slate-900">{{ right.name }} × {{ right.quantity }}</text>
|
||||
<text class="mt-1 block break-words text-xs text-slate-400">{{ right.fulfillment === 'third_party' ? '第三方出票' : '自营权益码' }} · 退款金额 {{ money(right.refundAllocation) }}</text>
|
||||
</view>
|
||||
<text class="shrink-0 rounded-full px-2.5 py-1 text-xs font-semibold" :class="entitlementMeta(right.status).className">{{ entitlementMeta(right.status).label }}</text>
|
||||
</view>
|
||||
<view v-if="!order.entitlements?.length" class="py-6 text-center text-sm text-slate-400">支付完成后生成履约权益</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="mt-[12px] box-border w-full min-w-0 rounded-[16px] bg-white p-[16px]">
|
||||
<text class="text-[16px] font-medium text-[#171717]">退款规则</text>
|
||||
<text class="mt-[10px] block text-[13px] leading-[20px] text-[#667085]">未使用的权益可按产品退款规则申请退款;已核销权益不支持退款,实际退款以审核结果为准。</text>
|
||||
<view class="box-border w-full min-w-0 rounded-2xl bg-white p-4">
|
||||
<text class="font-bold text-slate-900">费用信息</text>
|
||||
<view class="mt-3 flex flex-col gap-3 text-sm">
|
||||
<view class="flex min-w-0 justify-between gap-3">
|
||||
<text class="shrink-0 text-slate-400">商品金额</text>
|
||||
<text class="min-w-0 break-words text-right font-semibold text-slate-900">{{ money(productAmount) }}</text>
|
||||
</view>
|
||||
<view class="flex min-w-0 justify-between gap-3">
|
||||
<text class="shrink-0 text-slate-400">出行保险</text>
|
||||
<text class="min-w-0 break-words text-right font-semibold text-slate-900">{{ insuranceText }}</text>
|
||||
</view>
|
||||
<view class="flex min-w-0 justify-between gap-3 border-t border-slate-100 pt-3">
|
||||
<text class="shrink-0 text-slate-400">订单实付</text>
|
||||
<text class="min-w-0 break-words text-right text-lg font-bold text-orange-600">{{ money(order.amount ?? order.paidAmount) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="mt-3 flex min-w-0 justify-between gap-3 text-xs text-slate-400">
|
||||
<text class="shrink-0">支付时间</text>
|
||||
<text class="min-w-0 break-words text-right">{{ order.paidAt }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="box-border w-full min-w-0 rounded-2xl p-4 text-xs leading-5" :class="refundRuleClass">
|
||||
<view class="flex min-w-0 items-start gap-2">
|
||||
<uni-icons class="mt-0.5 shrink-0" type="info" size="16" :color="refundRuleIconColor" />
|
||||
<view class="min-w-0 flex-1">
|
||||
<text class="font-bold">退款规则</text>
|
||||
<text class="mt-1 block">{{ refundRuleText }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="h-[24px]" />
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<view class="box-border flex w-full min-w-0 gap-[12px] bg-white px-[16px] py-[12px] pb-[calc(env(safe-area-inset-bottom)+12px)]">
|
||||
<button v-if="hasCredential" class="m-0 min-w-0 flex-1 rounded-[24px] border border-[#7c3aed] bg-white p-0 text-[15px] leading-[48px] text-[#6d28d9]" @click="emit('credentials')">查看凭证</button>
|
||||
<button v-if="canRefund" class="m-0 min-w-0 flex-1 rounded-[24px] bg-[#7c3aed] p-0 text-[15px] leading-[48px] text-white" @click="emit('refund')">申请退款</button>
|
||||
<view class="absolute bottom-0 left-0 right-0 box-border flex w-full min-w-0 gap-3 border-t border-slate-200 bg-white p-4 pb-[calc(env(safe-area-inset-bottom)+16px)]">
|
||||
<button v-if="hasCredential" class="m-0 h-12 min-w-0 flex-1 rounded-full border border-blue-200 bg-blue-50 p-0 text-sm font-bold leading-[48px] text-blue-600" @tap="emit('credentials')">查看凭证</button>
|
||||
<button class="refund-action m-0 box-border h-12 min-w-0 flex-1 overflow-hidden rounded-full border border-transparent bg-orange-500 p-0 text-sm font-bold leading-[48px] text-white disabled:border-slate-200 disabled:bg-slate-200 disabled:text-slate-400" :disabled="!canRefund" @tap="canRefund && emit('refund')">{{ refundButtonText }}</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { ENTITLEMENT_STATUS_META, ORDER_STATUS_META } from '../data.js'
|
||||
|
||||
const props = defineProps({ order: { type: Object, required: true } })
|
||||
const emit = defineEmits(['credentials', 'refund'])
|
||||
const canRefund = computed(() => (props.order?.entitlements || []).some((right) => right.status === 'pending'))
|
||||
const hasCredential = computed(() => Boolean(props.order?.gateCredential || props.order?.selfCredential))
|
||||
const money = (value) => Number(value || 0).toFixed(2)
|
||||
const fulfillmentText = (value) => value === 'third_party' ? '第三方票务核验' : '景区自营权益'
|
||||
const rightStatus = (value) => ({ pending: '待使用', refunding: '退款中', used: '已使用', refunded: '已退款' }[value] || '待使用')
|
||||
const statusText = computed(() => ({ paid: '待使用', refunding: '退款中', completed: '已完成', refunded: '已退款' }[props.order?.status] || '待使用'))
|
||||
const fallbackStatus = { label: '未知状态', className: 'border-slate-200 bg-slate-50 text-slate-600' }
|
||||
const fallbackEntitlement = { label: '待使用', className: 'bg-blue-50 text-blue-700' }
|
||||
const statusMeta = computed(() => ORDER_STATUS_META[props.order?.status] || fallbackStatus)
|
||||
const entitlementMeta = (status) => ENTITLEMENT_STATUS_META[status] || fallbackEntitlement
|
||||
const isPackage = computed(() => props.order?.refundMode === 'all_or_nothing')
|
||||
const hasUsedEntitlement = computed(() => (props.order?.entitlements || []).some((right) => right.usedQuantity > 0 || right.status === 'used'))
|
||||
const packageHasUsed = computed(() => isPackage.value && hasUsedEntitlement.value)
|
||||
const isRefunding = computed(() => props.order?.status === 'refunding')
|
||||
const isRefunded = computed(() => props.order?.status === 'refunded')
|
||||
const travelerIdType = computed(() => props.order?.travelers?.[0]?.idType || (props.order?.idCard ? '身份证' : '—'))
|
||||
const refundableRights = computed(() => (props.order?.entitlements || []).filter((right) => right.status === 'pending' && Number(right.usedQuantity || 0) === 0))
|
||||
const canRefund = computed(() => (
|
||||
!['pending_payment', 'closed'].includes(props.order?.status)
|
||||
&& !isRefunding.value
|
||||
&& !isRefunded.value
|
||||
&& !packageHasUsed.value
|
||||
&& refundableRights.value.length > 0
|
||||
))
|
||||
const hasCredential = computed(() => Boolean(props.order?.gateCredential?.qrToken || props.order?.selfCredential))
|
||||
const productAmount = computed(() => (
|
||||
props.order?.productAmount
|
||||
?? Number(props.order?.amount || props.order?.paidAmount || 0) - Number(props.order?.insurance?.amount || props.order?.insuranceAmount || 0)
|
||||
))
|
||||
const insuranceTotal = computed(() => (
|
||||
props.order?.insurance?.amount
|
||||
?? props.order?.insuranceAmount
|
||||
?? Number(props.order?.insurance?.price || 0) * Number(props.order?.insurance?.quantity || 0)
|
||||
))
|
||||
const insuranceUnitPrice = computed(() => props.order?.insurance?.unitPrice ?? props.order?.insurance?.price ?? 0)
|
||||
const insuranceText = computed(() => props.order?.insurance
|
||||
? `${money(insuranceTotal.value)}(${money(insuranceUnitPrice.value)}/份 × ${props.order.insurance.quantity})`
|
||||
: '未购买')
|
||||
const refundRuleClass = computed(() => {
|
||||
if (packageHasUsed.value) return 'bg-rose-50 text-rose-700'
|
||||
if (isPackage.value) return 'bg-amber-50 text-amber-700'
|
||||
return 'bg-blue-50 text-blue-700'
|
||||
})
|
||||
const refundRuleIconColor = computed(() => packageHasUsed.value ? '#be123c' : isPackage.value ? '#b45309' : '#1d4ed8')
|
||||
const refundRuleText = computed(() => {
|
||||
if (packageHasUsed.value) return '套餐中已有门票、观光车或园内项目被核销,按照套餐规则整单不可退款。'
|
||||
if (isPackage.value) return '套餐内全部权益未使用,可申请整单全额退款;任一权益使用后整单不可退。'
|
||||
return '未使用权益可申请退款,已核销或已退款权益不可再次申请。'
|
||||
})
|
||||
const refundButtonText = computed(() => {
|
||||
if (isRefunding.value) return '退款处理中'
|
||||
if (isRefunded.value) return '已退款'
|
||||
return canRefund.value ? '申请退款' : '不可退款'
|
||||
})
|
||||
const money = (value) => `¥${Number(value || 0).toLocaleString('zh-CN')}`
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.refund-action::after {
|
||||
border: 0;
|
||||
border-radius: 9999px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -7,17 +7,22 @@
|
||||
<view class="flex min-w-0 justify-between gap-[12px]"><text class="shrink-0 text-[#667085]">订单号</text><text class="min-w-0 flex-1 break-all text-right text-[#344054]">{{ order.orderNo || order.id || '-' }}</text></view>
|
||||
<view class="mt-[12px] flex justify-between"><text class="text-[#667085]">退款状态</text><text class="text-[#d97706]">待审核</text></view>
|
||||
</view>
|
||||
<button class="mt-[28px] box-border w-full min-w-0 rounded-[24px] bg-[#7c3aed] p-0 text-[15px] leading-[48px] text-white" @click="emit('detail')">查看订单详情</button>
|
||||
<button class="mt-[28px] box-border w-full min-w-0 rounded-[24px] bg-orange-500 p-0 text-[15px] leading-[48px] text-white" @click="emit('detail')">查看订单详情</button>
|
||||
<button class="mt-[12px] box-border w-full min-w-0 rounded-[24px] border border-[#d0d5dd] bg-white p-0 text-[15px] leading-[48px] text-[#344054]" @click="emit('home')">返回景区首页</button>
|
||||
</view>
|
||||
|
||||
<view v-else class="flex h-full min-w-0 flex-col overflow-hidden bg-[#f8fafc]">
|
||||
<scroll-view scroll-y class="min-h-0 min-w-0 flex-1">
|
||||
<view class="box-border w-full min-w-0 px-[16px] py-[16px]">
|
||||
<view class="mb-[12px] box-border w-full min-w-0 rounded-[16px] bg-orange-50 p-[16px] text-orange-700">
|
||||
<text class="block text-[14px] font-semibold">{{ isPackage ? '套餐整单退款' : '按未使用权益退款' }}</text>
|
||||
<text class="mt-[4px] block text-[12px] leading-[20px]">{{ isPackage ? '套餐不支持拆项退款,本次将退回全部未使用权益对应的整单实付金额。' : '请选择需要退款的未使用权益,退款金额按购买时实际分摊金额计算。' }}</text>
|
||||
</view>
|
||||
|
||||
<view class="box-border w-full min-w-0 rounded-[16px] bg-white p-[16px]">
|
||||
<text class="text-[16px] font-medium text-[#171717]">选择退款权益</text>
|
||||
<view v-for="right in pendingRights" :key="right.id" class="mt-[12px] box-border flex w-full min-w-0 items-center rounded-[12px] border p-[12px]" :class="selectedIds.includes(right.id) ? 'border-[#7c3aed] bg-[#faf5ff]' : 'border-[#eaecf0] bg-white'" @click="toggleRight(right.id)">
|
||||
<view class="flex h-[20px] w-[20px] shrink-0 items-center justify-center rounded-full border" :class="selectedIds.includes(right.id) ? 'border-[#7c3aed] bg-[#7c3aed]' : 'border-[#d0d5dd] bg-white'"><uni-icons v-if="selectedIds.includes(right.id)" type="checkmarkempty" size="14" color="#ffffff" /></view>
|
||||
<view v-for="right in pendingRights" :key="right.id" class="mt-[12px] box-border flex w-full min-w-0 items-center rounded-[12px] border p-[12px]" :class="selectedIds.includes(right.id) ? 'border-orange-400 bg-orange-50' : 'border-[#eaecf0] bg-white'" @click="toggleRight(right.id)">
|
||||
<view class="flex h-[20px] w-[20px] shrink-0 items-center justify-center rounded-full border" :class="selectedIds.includes(right.id) ? 'border-orange-500 bg-orange-500' : 'border-[#d0d5dd] bg-white'"><uni-icons v-if="selectedIds.includes(right.id)" type="checkmarkempty" size="14" color="#ffffff" /></view>
|
||||
<view class="ml-[10px] min-w-0 flex-1"><text class="block break-words text-[14px] text-[#344054]">{{ right.name }}</text><text class="mt-[3px] block break-words text-[12px] text-[#98a2b3]">{{ right.quantity || 0 }}份,可退 ¥{{ money((right.refundAllocation || 0) * (right.quantity || 0)) }}</text></view>
|
||||
</view>
|
||||
<text v-if="!pendingRights.length" class="mt-[14px] block text-[14px] text-[#98a2b3]">当前没有可申请退款的权益</text>
|
||||
@@ -26,19 +31,19 @@
|
||||
<view class="mt-[12px] box-border w-full min-w-0 rounded-[16px] bg-white p-[16px]">
|
||||
<text class="text-[16px] font-medium text-[#171717]">退款原因</text>
|
||||
<view v-for="reason in reasons" :key="reason" class="mt-[12px] flex min-w-0 items-start" @click="selectedReason = reason">
|
||||
<view class="mt-[1px] flex h-[20px] w-[20px] shrink-0 items-center justify-center rounded-full border" :class="selectedReason === reason ? 'border-[#7c3aed] bg-[#7c3aed]' : 'border-[#d0d5dd] bg-white'"><view v-if="selectedReason === reason" class="h-[7px] w-[7px] rounded-full bg-white" /></view>
|
||||
<view class="mt-[1px] flex h-[20px] w-[20px] shrink-0 items-center justify-center rounded-full border" :class="selectedReason === reason ? 'border-orange-500 bg-orange-500' : 'border-[#d0d5dd] bg-white'"><view v-if="selectedReason === reason" class="h-[7px] w-[7px] rounded-full bg-white" /></view>
|
||||
<text class="ml-[10px] min-w-0 flex-1 break-words text-[14px] leading-[21px] text-[#344054]">{{ reason }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="mt-[12px] box-border w-full min-w-0 rounded-[16px] bg-white p-[16px]">
|
||||
<view class="flex min-w-0 items-center justify-between gap-[12px]"><text class="text-[15px] text-[#667085]">预计退款金额</text><text class="shrink-0 text-[22px] font-semibold text-[#ef4444]">¥{{ money(refundAmount) }}</text></view>
|
||||
<view class="flex min-w-0 items-center justify-between gap-[12px]"><text class="text-[15px] text-[#667085]">预计退款金额</text><text class="shrink-0 text-[22px] font-semibold text-orange-600">¥{{ money(refundAmount) }}</text></view>
|
||||
<text class="mt-[8px] block text-[12px] text-[#98a2b3]">实际到账金额以审核结果为准</text>
|
||||
</view>
|
||||
<view class="h-[24px]" />
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="box-border w-full min-w-0 bg-white px-[16px] py-[12px] pb-[calc(env(safe-area-inset-bottom)+12px)]"><button class="box-border w-full min-w-0 rounded-[24px] bg-[#7c3aed] p-0 text-[15px] leading-[48px] text-white" @click="submitRefund">提交退款申请</button></view>
|
||||
<view class="box-border w-full min-w-0 bg-white px-[16px] py-[12px] pb-[calc(env(safe-area-inset-bottom)+12px)]"><button class="box-border w-full min-w-0 rounded-[24px] bg-orange-500 p-0 text-[15px] leading-[48px] text-white" @click="submitRefund">提交退款申请</button></view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -54,6 +59,7 @@ const props = defineProps({
|
||||
const emit = defineEmits(['submit', 'detail', 'home'])
|
||||
const selectedIds = ref([])
|
||||
const selectedReason = ref('')
|
||||
const isPackage = computed(() => props.order?.refundMode === 'all_or_nothing')
|
||||
const pendingRights = computed(() => (props.order?.entitlements || []).filter((right) => right.status === 'pending'))
|
||||
const refundAmount = computed(() => calculateRefundAmount(props.order, selectedIds.value))
|
||||
const money = (value) => Number(value || 0).toFixed(2)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<view class="flex h-screen min-w-0 flex-col overflow-hidden bg-[#f8fafc]">
|
||||
<TopNavBar
|
||||
title="查看凭证"
|
||||
subtitle="入园及园内权益核验"
|
||||
:subtitle="ticketStore.currentOrder?.orderNo || ''"
|
||||
title-align="left"
|
||||
background="#ffffff"
|
||||
/>
|
||||
|
||||
@@ -13,6 +13,13 @@ export const ORDER_STATUS_META = {
|
||||
exception: { label: '异常', className: 'border-rose-200 bg-rose-50 text-rose-700' },
|
||||
}
|
||||
|
||||
export const ENTITLEMENT_STATUS_META = {
|
||||
pending: { label: '待使用', className: 'bg-blue-50 text-blue-700' },
|
||||
used: { label: '已使用', className: 'bg-emerald-50 text-emerald-700' },
|
||||
refunding: { label: '退款中', className: 'bg-orange-50 text-orange-700' },
|
||||
refunded: { label: '已退款', className: 'bg-slate-100 text-slate-600' },
|
||||
}
|
||||
|
||||
export const SCENIC_SPOT = {
|
||||
id: 'libo-xiaoqikong',
|
||||
name: '荔波小七孔景区',
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
|
||||
test('provides coherent default visitor ticket data', async () => {
|
||||
const data = await import('./data.js')
|
||||
const { AVAILABLE_DATES, GROUPS, INITIAL_TRAVELERS, INSURANCE, ORDER_STATUS_META, PLACEHOLDER_IMAGE, PRODUCTS, REFUND_REASONS, SCENIC_SPOT } = data
|
||||
const { AVAILABLE_DATES, ENTITLEMENT_STATUS_META, GROUPS, INITIAL_TRAVELERS, INSURANCE, ORDER_STATUS_META, PLACEHOLDER_IMAGE, PRODUCTS, REFUND_REASONS, SCENIC_SPOT } = data
|
||||
const { INITIAL_ORDERS } = data
|
||||
|
||||
assert.equal(PLACEHOLDER_IMAGE, 'https://one-feel-bucket.oss-cn-guangzhou.aliyuncs.com/oneFeel/2082673896938569729.jpg')
|
||||
@@ -86,6 +86,10 @@ test('provides coherent default visitor ticket data', async () => {
|
||||
exception: '异常',
|
||||
},
|
||||
)
|
||||
assert.deepEqual(
|
||||
Object.fromEntries(Object.entries(ENTITLEMENT_STATUS_META).map(([status, meta]) => [status, meta.label])),
|
||||
{ pending: '待使用', used: '已使用', refunding: '退款中', refunded: '已退款' },
|
||||
)
|
||||
assert.equal(INITIAL_ORDERS.length, 11)
|
||||
assert.deepEqual(
|
||||
INITIAL_ORDERS.map(({ orderNo, productName, status, amount }) => [orderNo, productName, status, amount]),
|
||||
|
||||
@@ -94,6 +94,82 @@ test("order list matches the visitor prototype card layout and fields", async ()
|
||||
assert.match(component, /type="right"/);
|
||||
});
|
||||
|
||||
test("order detail matches the visitor prototype sections and actions", async () => {
|
||||
const component = await readTicketFile("components/OrderDetail.vue");
|
||||
const page = await readTicketFile("order-detail.vue");
|
||||
|
||||
assert.match(component, /box-border flex w-full min-w-0 flex-col gap-3 p-4 pb-28/);
|
||||
assert.match(component, /rounded-2xl bg-white p-4/);
|
||||
assert.match(component, /ORDER_STATUS_META/);
|
||||
assert.match(component, /rounded-full border px-2\.5 py-1 text-xs font-semibold/);
|
||||
assert.match(component, /:class="statusMeta\.className"/);
|
||||
assert.match(component, /order\.orderNo/);
|
||||
assert.match(component, /使用信息/);
|
||||
assert.match(component, /使用日期/);
|
||||
assert.match(component, /使用时段/);
|
||||
assert.match(component, /出行人/);
|
||||
assert.match(component, /travelerIdType/);
|
||||
assert.match(component, /联系手机/);
|
||||
assert.match(component, /order\.customerName/);
|
||||
assert.match(component, /order\.phone/);
|
||||
assert.doesNotMatch(component, /当前状态|statusDescription|<uni-icons type="list"/);
|
||||
assert.ok(component.indexOf("order.productName") < component.indexOf("order.orderNo"));
|
||||
assert.match(component, /第三方出票/);
|
||||
assert.match(component, /自营权益码/);
|
||||
assert.match(component, /退款金额/);
|
||||
assert.match(component, /ENTITLEMENT_STATUS_META/);
|
||||
assert.match(component, /商品金额/);
|
||||
assert.match(component, /出行保险/);
|
||||
assert.match(component, /订单实付/);
|
||||
assert.match(component, /套餐内全部权益未使用,可申请整单全额退款/);
|
||||
assert.match(component, /套餐中已有门票、观光车或园内项目被核销/);
|
||||
assert.match(component, /:disabled="!canRefund"/);
|
||||
assert.match(component, /退款处理中/);
|
||||
assert.match(component, /不可退款/);
|
||||
assert.match(component, /refund-action/);
|
||||
assert.match(component, /\.refund-action::after/);
|
||||
assert.match(component, /border:\s*(?:0|none)/);
|
||||
assert.match(page, /subtitle="查看使用信息与订单权益"/);
|
||||
assert.doesNotMatch(page, /:subtitle="ticketStore\.currentOrder\?\.orderNo \|\| ''"/);
|
||||
});
|
||||
|
||||
test("credentials match the visitor prototype and render real QR codes", async () => {
|
||||
const component = await readTicketFile("components/CredentialsView.vue");
|
||||
const page = await readTicketFile("credentials.vue");
|
||||
|
||||
assert.match(component, /import Qrcode from "@\/components\/Qrcode\/index\.vue"/);
|
||||
assert.equal((component.match(/<Qrcode/g) || []).length, 2);
|
||||
assert.match(component, /cid="ticket-gate-credential"/);
|
||||
assert.match(component, /cid="ticket-self-credential"/);
|
||||
assert.match(component, /:val="gateQrValue"/);
|
||||
assert.match(component, /:val="selfQrValue"/);
|
||||
assert.match(component, /:load-make="true"/);
|
||||
assert.match(component, /order\?\.gateCredential\?\.qrToken \|\| order\?\.gateCredential\?\.ticketNo \|\| order\?\.id/);
|
||||
assert.match(component, /order\?\.selfCredential\?\.qrToken \|\| order\?\.selfCredential\?\.credentialNo \|\| order\?\.id/);
|
||||
assert.match(component, /第三方票务凭证/);
|
||||
assert.match(component, /园内权益凭证/);
|
||||
assert.match(component, /多个自营二销项目共用此码,员工扫码后选择项目核销/);
|
||||
assert.match(component, /ENTITLEMENT_STATUS_META/);
|
||||
assert.match(component, /rounded-2xl bg-white p-4 shadow-sm/);
|
||||
assert.doesNotMatch(component, /gateGrid|selfGrid|makeGrid/);
|
||||
assert.doesNotMatch(component, /bg-\[#5b21b6\]|from-violet-700|to-indigo-700/);
|
||||
assert.match(page, /:subtitle="ticketStore\.currentOrder\?\.orderNo \|\| ''"/);
|
||||
});
|
||||
|
||||
test("refund flow shows the package notice and uses the orange visitor theme", async () => {
|
||||
const component = await readTicketFile("components/RefundFlow.vue");
|
||||
|
||||
assert.match(component, /套餐整单退款/);
|
||||
assert.match(component, /套餐不支持拆项退款,本次将退回全部未使用权益对应的整单实付金额。/);
|
||||
assert.match(component, /refundMode === 'all_or_nothing'/);
|
||||
assert.match(component, /bg-orange-50/);
|
||||
assert.match(component, /border-orange-400/);
|
||||
assert.match(component, /border-orange-500 bg-orange-500/);
|
||||
assert.match(component, /text-orange-600/);
|
||||
assert.match(component, /bg-orange-500/);
|
||||
assert.doesNotMatch(component, /#7c3aed|#faf5ff/);
|
||||
});
|
||||
|
||||
test("traveler manager matches the approved prototype details", async () => {
|
||||
const component = await readTicketFile("components/TravelerManager.vue");
|
||||
const page = await readTicketFile("travelers.vue");
|
||||
|
||||
Reference in New Issue
Block a user