You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

643 lines
16 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<view class="pay-bg" :class="{ 'wechat-browser': isWeixinBrowser }">
<view class="fixed-nav" v-if="!isWeixinBrowser">
<NavBar title="订单详情" />
</view>
<view class="pay-scroll">
<!-- 预约信息 -->
<view class="pay-section">
<view class="pay-title">预约信息</view>
<view class="pay-row"
><text>预约日期</text
><text>{{
formatChinaDate(
item.batch && item.batch.created_at
? item.batch.created_at
: item.created_at
)
}}</text></view
>
<view class="pay-row"
><text>预约批次</text
><text>{{
item.batch && item.batch.name ? item.batch.name : "-"
}}</text></view
>
<view class="pay-row"
><text>航行方向</text><text>{{ item.direction_name }}</text></view
>
<view class="pay-row">
<text>预约状态</text>
<text class="pay-status" :class="item.status">{{
getStatusText(item.status)
}}</text>
</view>
<view class="pay-row"
><text>支付金额</text><text>¥{{ item.price }}</text></view
>
</view>
<!-- 船舶信息 -->
<view class="pay-section">
<view class="pay-title">船舶信息</view>
<view class="pay-row"
><text>船舶编号</text><text>{{ item.ship.ship_number }}</text></view
>
<view class="pay-row"
><text>总长度</text><text>{{ item.ship.total_length }}米</text></view
>
<view class="pay-row"
><text>型宽</text><text>{{ item.ship.total_width }}米</text></view
>
<view class="pay-row"
><text>型深</text><text>{{ item.ship.molded_depth }}米</text></view
>
<view class="pay-row"
><text>载重</text><text>{{ item.ship.total_tonnage }}吨</text></view
>
<view class="pay-row"
><text>类型</text
><text>{{
item.ship && item.ship.ship_type
? getShipTypeName(item.ship.ship_type)
: ""
}}</text></view
>
</view>
<!-- 票价信息 -->
<view
class="pay-section"
v-if="item.status === 'paid' || item.status === 'completed'"
>
<view class="pay-title">票价信息</view>
<view class="pay-row"
><text>过闸费用</text><text>¥{{ item.price }}</text></view
>
<view class="pay-row"
><text>按吨位计费</text
><text>{{ item.ship.total_tonnage }}吨</text></view
>
<view class="pay-row pay-total">
<text>总计</text>
<text class="pay-total-num">¥{{ item.price }}</text>
</view>
</view>
<!-- 支付方式 -->
<view
class="pay-section"
v-if="item.status === 'unpaid' || item.status === 'approved'"
>
<view class="pay-title">扫码支付</view>
<view class="pay-qrcode-box">
<!-- 二维码组件text 为内容size 为尺寸(单位 px -->
<a :href="qrContent">
<s-qrcode
ref="qrcode"
v-if="qrContent"
canvas-id="qrcode"
:value="qrContent"
:sizeUnit="'rpx'"
:size="260"
color="#333333"
bgColor="#ffffff"
margin="10"
@complete="handleComplete"
></s-qrcode>
</a>
</view>
<view class="pay-qrcode-tip">点击二维码支付或保存图片到相册</view>
</view>
</view>
<view class="pay-bottom-bar">
<template v-if="item.status === 'unpaid' || item.status === 'approved'">
<!-- <view class="pay-bottom-tip">
<text class="pay-bottom-clock">🕒</text>
<text class="pay-bottom-tip-text">
请在 <text class="pay-bottom-time">14:06</text> 内完成支付
</text>
</view> -->
<view class="pay-bottom-btns">
<button class="pay-pay-btn" @click="saveImage">保存图片</button>
</view>
</template>
<template v-else-if="item.status === 'pending'">
<view class="pay-bottom-btns">
<button class="pay-cancel-btn" @click="handleCancel">取消预约</button>
</view>
</template>
<template v-else-if="item.status === 'rejected'">
<view class="pay-bottom-btns">
<button class="pay-cancel-btn" @click="handleReReserve">
</button>
</view>
</template>
</view>
</view>
</template>
<script>
import NavBar from "@/components/NavBar.vue";
import { base } from "@/common/util.js";
import { API } from "@/config/index.js";
import sQrcode from "@/uni_modules/Sansnn-uQRCode/components/u-qrcode/u-qrcode.vue";
export default {
name: "PayOrderDetailPage",
components: {
NavBar,
sQrcode,
},
data() {
return {
isWeixinBrowser: false,
qrcodeUrl: "",
payType: "wechat",
item: {},
shipTypeEnum: [],
reservationStatusEnum: [],
qrContent: "",
};
},
onLoad(options) {
if (options.item) {
try {
this.item = JSON.parse(decodeURIComponent(options.item));
console.log("this.item", this.item);
} catch (e) {
console.error("Failed to parse item:", e);
this.item = {};
}
}
console.log(this.item);
// #ifdef H5
this.isWeixinBrowser = /MicroMessenger/i.test(navigator.userAgent);
// #endif
},
onShow() {
this.fetchShipTypeEnum().then(() => {
if (this.item.id && this.item.status === "unpaid") {
this.fetchQrcode(this.item.id);
}
});
this.fetchReservationStatusEnum();
},
methods: {
formatChinaDate: base.formatChinaDate,
async fetchQrcode(id) {
const token = uni.getStorageSync("token");
if (!token || !id) return;
try {
const res = await new Promise((resolve, reject) => {
uni.request({
url: `${API.GET_PAYMENT_QRCODE}`,
data: {
reservation_id: id,
token: token,
},
method: "POST",
success: resolve,
fail: reject,
});
});
if (res.data && res.data.errcode === 0) {
const payLink = res.data.data.payment_qrcode;
// 如果返回的是链接,则生成二维码
console.log("payLink", payLink);
// this.generateQRCode(payLink);
this.qrContent = payLink;
} else {
uni.showToast({
title: res.data.errmsg || "获取二维码失败",
icon: "none",
});
}
} catch (e) {}
},
saveImage() {
//e 图片地址
// 非H5环境中
// #ifndef H5
uni.saveImageToPhotosAlbum({
filePath: this.qrcodeUrl, //图片url
success: () => {
uni.showToast({
title: "保存成功",
icon: "success",
});
},
});
// #endif
//H5环境中也可以提示长按屏幕保存让用户操作不执行以下事件
// #ifdef H5
if (typeof plus !== "undefined") {
// 当前运行在H5+环境中
var down = plus.downloader.createDownload(this.qrcodeUrl, {}, function (e, a) {
plus.gallery.save(
e.filename,
function (e) {
uni.showToast({
title: "保存成功",
mask: true,
});
console.log("下载成功");
},
function (e) {
console.log("下载失败,请重试");
}
);
});
down.start();
} else {
var oA = document.createElement("a");
oA.download = "pay_code.png"; // 设置下载的文件名,默认是'下载'
oA.href = this.qrcodeUrl; //图片url
document.body.appendChild(oA);
oA.click();
oA.remove(); // 下载之后把创建的元素删除
}
// #endif
},
handleComplete(e) {
console.log("handleComplete", e);
if (e && e.success) {
this.$refs.qrcode.toTempFilePath({
success: (res) => {
console.log(res);
this.qrcodeUrl = res.tempFilePath;
},
fail: (err) => {
console.log(err);
},
complete: () => {
console.log("complete");
},
});
}
},
async fetchShipTypeEnum() {
const token = uni.getStorageSync("token");
if (!token) return;
try {
const res = await new Promise((resolve, reject) => {
uni.request({
url: `${API.SHIP_PROPERTY_ENUM}?token=${token}`,
method: "GET",
success: resolve,
fail: reject,
});
});
if (res.data && res.data.errcode === 0) {
const shipTypeRaw = res.data.data.ship_type || {};
if (Array.isArray(shipTypeRaw)) {
this.shipTypeEnum = shipTypeRaw;
} else {
this.shipTypeEnum = Object.keys(shipTypeRaw).map((label) => ({
label,
value: shipTypeRaw[label],
}));
}
}
} catch (e) {
console.error("Failed to fetch ship type enum:", e);
}
},
getShipTypeName(type) {
const found = this.shipTypeEnum.find(
(item) => item.value === type || item.value == type
);
return found ? found.label : type;
},
handleLongPress() {
// 获取当前图片信息
uni.getImageInfo({
src: this.qrcodeUrl,
success: (imageInfo) => {
// 使用图片路径进行扫码
uni.scanCode({
scanType: ["qrCode"],
onlyFromCamera: false,
path: imageInfo.path,
success: (res) => {
console.log("扫码结果:", res);
if (res.result) {
uni.showToast({
title: "扫码成功",
icon: "success",
});
// 这里可以处理扫码结果,比如跳转到支付页面
// uni.navigateTo({
// url: `/pages/payment/index?url=${encodeURIComponent(res.result)}`
// });
}
},
fail: (err) => {
console.error("扫码失败:", err);
uni.showToast({
title: "扫码失败",
icon: "none",
});
},
});
},
fail: (err) => {
console.error("获取图片信息失败:", err);
uni.showToast({
title: "获取图片失败",
icon: "none",
});
},
});
},
handleCancel() {
uni.showModal({
title: "提示",
content: "确定要取消该预约吗?",
confirmText: "确定",
cancelText: "再想想",
success: (res) => {
if (res.confirm) {
// 这里写取消预约的逻辑
uni.showToast({ title: "已取消预约", icon: "success" });
}
},
});
},
handleReReserve() {
uni.navigateTo({ url: "/pages/reservation/index" });
},
handlePay() {
//POST调用模拟支付接口
uni.request({
url: `${API.FAKE_PAY}`,
method: "POST",
data: {
reservation_id: this.item.id,
token: uni.getStorageSync("token"),
},
success: (res) => {
console.log("模拟支付结果:", res);
if (res.data && res.data.errcode === 0) {
uni.showToast({ title: "支付成功", icon: "success" });
uni.navigateBack();
} else {
uni.showToast({
title: res.data.errmsg || "支付失败",
icon: "none",
});
}
},
fail: (err) => {
console.error("模拟支付失败:", err);
},
});
},
async fetchReservationStatusEnum() {
const token = uni.getStorageSync("token");
if (!token) return;
try {
const res = await new Promise((resolve, reject) => {
uni.request({
url: `${API.RESERVATION_STATUS_ENUM}?token=${token}`,
method: "GET",
success: resolve,
fail: reject,
});
});
if (res.data && res.data.errcode === 0) {
this.reservationStatusEnum = res.data.data;
}
} catch (e) {
console.error("Failed to fetch reservation status enum:", e);
}
},
getStatusText(status) {
if (this.reservationStatusEnum && this.reservationStatusEnum[status]) {
return this.reservationStatusEnum[status].label;
}
return status;
},
},
};
</script>
<style lang="scss" scoped>
@import "@/styles/common.scss";
.pay-bg {
min-height: 100vh;
background: linear-gradient(180deg, #cbe6ff 0%, #f6faff 100%);
padding-bottom: 32rpx;
}
.wechat-browser {
padding-top: 20rpx;
}
.wechat-browser .pay-scroll {
padding-top: 0;
}
.fixed-nav {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 100;
background: linear-gradient(180deg, #cbe6ff 0%, #f6faff 100%);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
height: 90px;
}
.pay-scroll {
padding-top: 110px;
padding-bottom: 160rpx;
}
.pay-section {
background: #fff;
border-radius: 24rpx;
margin: 0 24rpx 32rpx 24rpx;
box-shadow: 0 4rpx 16rpx rgba(59, 124, 255, 0.08);
padding: 32rpx 24rpx 8rpx 24rpx;
}
.pay-title {
@include font-primary;
font-weight: bold;
color: #222;
margin-bottom: 24rpx;
}
.pay-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 18rpx 0;
border-bottom: 1rpx solid #f2f4f8;
text:first-child {
@include font-secondary;
color: $font-color-primary;
}
text:last-child {
@include font-secondary;
color: $font-color-secondary;
}
}
.pay-row:last-child {
border-bottom: none;
}
.pay-status {
background: #217aff;
color: #fff !important; // 强制纯白色
border-radius: 24rpx;
padding: 4rpx 24rpx;
font-size: 24rpx;
&.pending {
background: #ff9800;
}
&.rejected {
background: #ff4d4f;
}
&.unpaid,
&.approved {
background: #217aff;
}
&.paid {
background: #22c58b;
}
}
.pay-total {
font-weight: bold;
}
.pay-total-num {
color: #217aff;
font-size: 32rpx;
}
.pay-paytype {
gap: 16rpx;
}
.pay-label {
display: flex;
align-items: center;
}
.pay-icon {
width: 36rpx;
height: 36rpx;
margin-right: 20rpx;
}
.pay-paytype text {
margin-left: 0; /* 确保文字紧跟图标 */
}
.pay-radio-group {
display: flex;
flex-direction: column;
gap: 12rpx;
}
.pay-bottom-bar {
position: fixed;
left: 0;
right: 0;
bottom: 0;
background: #fff;
box-shadow: 0 -2rpx 16rpx rgba(59, 124, 255, 0.08);
padding: 24rpx 24rpx 32rpx 24rpx;
z-index: 999;
display: flex;
flex-direction: column;
align-items: center;
}
.pay-bottom-tip {
display: flex;
align-items: center;
justify-content: center;
font-size: 26rpx;
margin-bottom: 18rpx;
}
.pay-bottom-clock-box {
width: 40rpx;
height: 40rpx;
border: 2rpx solid #217aff;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 12rpx;
}
.pay-bottom-clock {
color: #217aff;
font-size: 28rpx;
}
.pay-bottom-tip-text {
color: #222;
}
.pay-bottom-time {
color: #217aff;
font-weight: bold;
}
.pay-bottom-btns {
display: flex;
gap: 24rpx;
justify-content: center;
width: 100%;
}
.pay-cancel-btn {
min-width: 240rpx;
height: 72rpx;
border-radius: 36rpx;
background: #f5f7fa;
color: #222;
font-size: 28rpx;
border: none;
outline: none;
&::after {
border: none;
}
}
.pay-pay-btn {
min-width: 320rpx;
height: 72rpx;
border-radius: 36rpx;
background: #217aff;
color: #fff;
font-size: 28rpx;
font-weight: 500;
border: none;
outline: none;
&::after {
border: none;
}
}
.pay-qrcode-box {
display: flex;
justify-content: center;
align-items: center;
margin: 32rpx 0;
}
.pay-qrcode-img {
width: 260rpx;
height: 260rpx;
background: #fff;
border-radius: 16rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.06);
}
.pay-qrcode-tip {
text-align: center;
color: #888;
font-size: 26rpx;
margin-top: 12rpx;
}
.pay-detail-btn {
min-width: 240rpx;
height: 72rpx;
border-radius: 36rpx;
background: #e4f3fe;
color: #217aff;
font-size: 28rpx;
border: none;
outline: none;
&::after {
border: none;
}
}
</style>