alipay-emulator/pages/balance/transfer/transfer.vue

1003 lines
21 KiB
Vue

<template>
<!-- 水印 -->
<view v-if="$isVip()">
<watermark dark="light" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark')">
<c-lottie ref="cLottieRef" :src='$watermark()' width="94px" height='74px' :loop="true"></c-lottie>
</liu-drag-button>
</view>
<view class="container">
<!-- 蓝色背景区域 -->
<view class="header-section">
<nav-bar bgColor="transparent" :isBack="false" tipLayerType="transfer-tip" isTipLayer tipLayerText="修改转账数据"
:buttonGroup="buttonGroup" @button-click="util.clickTitlePopupButton">
<template v-slot:right>
<view class="nav-link w100" style="text-align: right;white-space: nowrap;padding: 0;">
<text @click="util.goBack()">回首页</text>
</view>
</template>
<template v-slot:center>
<view class="status-box">
<image class="image" src="/static/image/balance/transfer/success.png"></image>
<text class="status-text">转账成功</text>
</view>
</template>
</nav-bar>
<!-- 转账状态信息 -->
<view class="success-info">
<view class="amount-box">
<text class="symbol">¥</text>
<text class="amount alipay-font">{{ Number(transferData.amount).toFixed(2) }}</text>
</view>
<!-- 收款方/付款方式 -->
<view class="details-box">
<view class="detail-item">
<text class="label">收款方</text>
<text class="value">{{ transferData.recipient }}</text>
</view>
<view class="detail-item">
<text class="label">付款方式</text>
<text class="value">{{ transferData.paymentMethod }}</text>
</view>
</view>
</view>
<!-- 立即通知收款人卡片 -->
<view class="card notification-card">
<view class="card-header">
<text class="card-title">立即通知收款人</text>
<view class="card-sub-title">
<text>预约转账</text>
<uni-icons type="right" size="16" color="#969696"></uni-icons>
</view>
</view>
<view class="card-item-bg">
<view class="user-info">
<view class="avatar-box">
<image v-if="transferData.avatar" class="avatar" :src="transferData.avatar"
mode="aspectFill">
</image>
<image v-else class="avatar" src="/static/image/balance/transfer/add-img.png"
mode="aspectFill" @click="openEditPopup">
</image>
</view>
<view class="user-details">
<view class="name-box">
<text class="name">{{ transferData.recipient }}</text>
<text class="remark-link">备注</text>
</view>
<text class="desc">信息准确,对账无忧</text>
</view>
</view>
<view class="notify-btn-box">
<view class="notify-btn">通知TA</view>
</view>
</view>
</view>
<view class="promo-card">
<view class="promo-left-box">
<image class="img" src="/static/image/balance/transfer/card-img-2.png" mode="aspectFill"></image>
</view>
<view class="promo-right">
<view class="promo-content">
<text class="promo-title">{{ transferData.promoTitle }}</text>
<view class="promo-tags">
<text class="tag">淘宝闪购</text>
<text class="tag-desc">{{ transferData.promoTagDesc }}</text>
</view>
</view>
<view class="promo-right-box">
<view class="price-box">
<text class="p-num alipay-font">{{ transferData.promoPrice }}</text>
<text class="p-unit">元</text>
</view>
<view class="free-btn">免费领</view>
</view>
</view>
</view>
</view>
<!-- 白色卡片区域 -->
<view class="content-section">
<!-- 飞猪广告 -->
<view class="card fliggy-card">
<image class="banner-img" src="/static/image/balance/transfer/bottom-banner.png" mode="widthFix">
</image>
<view class="yaoyiyao-container">
<view class="yaoyiyao-box">
<image class="img" src="/static/image/balance/transfer/yaoyiyao.png" mode="widthFix"></image>
<text class="text">摇动或点击查看详情</text>
</view>
</view>
<view class="banner-footer">
<view class="banner-info">
<view class="logo-box">
<image class="logo" src="/static/image/balance/transfer/feizhu.png" mode="aspectFit">
</image>
</view>
<view class="text-content">
<text class="b-title">飞猪旅行</text>
<text class="b-desc">飞猪出行 超多折扣</text>
</view>
</view>
<view class="b-btn">领优惠</view>
</view>
</view>
</view>
<!-- 底部操作区 -->
<view class="footer-section">
<view class="finish-btn" @click="goBack">完成</view>
</view>
<!-- 编辑弹窗 -->
<uni-popup ref="editPopup" type="center">
<view class="edit-modal">
<view class="modal-title">编辑转账数据</view>
<view class="modal-content">
<scroll-view scroll-y="true" class="modal-scroll">
<view class="form-item">
<text class="require-dot">*</text>
<text class="label">选择头像</text>
<view class="avatar-picker" @click="chooseAvatar">
<image v-if="tempEditData.avatar" :src="tempEditData.avatar" mode="aspectFill"></image>
<image v-else class="dialog-avatar" src="/static/image/balance/transfer/add-img.png"
mode="aspectFill">
</image>
</view>
</view>
<view class="form-item">
<text class="require-dot">*</text>
<text class="label">收款方</text>
<uni-easyinput class="input" v-model="tempEditData.recipient" :inputBorder="false"
placeholder="请输入收款方" />
</view>
<view class="form-item">
<text class="require-dot">*</text>
<text class="label">付款方式</text>
<uni-easyinput class="input" v-model="tempEditData.paymentMethod" :inputBorder="false"
placeholder="请输入付款方式" />
</view>
<!-- 闪购神券配置 -->
<text class="form-title-desc">闪购神券配置</text>
<view class="form-item">
<text class="require-dot"></text>
<text class="label">闪购标题</text>
<uni-easyinput class="input" v-model="tempEditData.promoTitle" :inputBorder="false"
placeholder="请输入闪购标题" />
</view>
<view class="form-item">
<text class="require-dot"></text>
<text class="label">标签说明</text>
<uni-easyinput class="input" v-model="tempEditData.promoTagDesc" :inputBorder="false"
placeholder="请输入标签说明" />
</view>
<view class="form-item price-item">
<text class="require-dot"></text>
<text class="label">神券金额</text>
<view class="input-with-unit">
<uni-easyinput class="input" v-model="tempEditData.promoPrice" :inputBorder="false"
placeholder="请输入神券金额" />
<text class="unit">元</text>
</view>
</view>
</scroll-view>
</view>
<view class="modal-footer">
<view class="btn cancel" @click="closeEditPopup">取消</view>
<view class="btn confirm" @click="confirmEdit">确定</view>
</view>
</view>
</uni-popup>
</view>
</template>
<script setup>
import {
ref,
onMounted,
reactive,
getCurrentInstance
} from 'vue';
import {
onShow,
onLoad
} from '@dcloudio/uni-app';
import {
util,
numberUtil
} from '@/utils/common.js';
const { appContext, proxy } = getCurrentInstance();
const statusBarHeight = ref(0);
const editPopup = ref(null);
const STORAGE_KEY = 'alipay_transfer_data';
const transferData = reactive({
amount: '10000',
recipient: '小王(*王)',
paymentMethod: '工商银行卡(9999)',
avatar: '',
promoTitle: '零售购物券',
promoTagDesc: '满29元可用',
promoPrice: '8'
});
onLoad(() => {
// 进入转账页面埋点
proxy.$apiUserEvent('all', {
type: 'click',
key: 'transfer',
value: "转账"
})
})
// 从缓存加载数据
const loadStorageData = () => {
const savedData = uni.getStorageSync(STORAGE_KEY);
if (savedData) {
Object.assign(transferData, savedData);
}
};
loadStorageData();
const tempEditData = reactive({
recipient: '',
paymentMethod: '',
avatar: '',
promoTitle: '',
promoTagDesc: '',
promoPrice: ''
});
const buttonGroup = [{
name: "修改转账数据",
click: () => {
openEditPopup();
}
}]
const openEditPopup = () => {
tempEditData.recipient = transferData.recipient;
tempEditData.paymentMethod = transferData.paymentMethod;
tempEditData.avatar = transferData.avatar;
tempEditData.promoTitle = transferData.promoTitle;
tempEditData.promoTagDesc = transferData.promoTagDesc;
tempEditData.promoPrice = transferData.promoPrice;
editPopup.value.open();
};
const closeEditPopup = () => {
editPopup.value.close();
};
/**
* 将图片保存到本地持久化存储
*/
const saveImageToLocal = (tempFilePath) => {
return new Promise((resolve) => {
// #ifdef APP-PLUS
// 如果是已经持久化的路径或是静态资源,直接返回
if (!tempFilePath || tempFilePath.startsWith('_doc') || tempFilePath.startsWith('/static')) {
return resolve(tempFilePath);
}
uni.saveFile({
tempFilePath: tempFilePath,
success: (res) => {
resolve(res.savedFilePath);
},
fail: (err) => {
console.error('图片持久化失败:', err);
resolve(tempFilePath);
}
});
// #endif
// #ifndef APP-PLUS
resolve(tempFilePath);
// #endif
});
};
/**
* 删除本地持久化文件
*/
const removeLocalFile = (filePath) => {
// #ifdef APP-PLUS
if (filePath && filePath.startsWith('_doc')) {
uni.removeSavedFile({
filePath: filePath,
success: () => {
console.log('本地文件删除成功:', filePath);
},
fail: (err) => {
console.warn('本地文件删除失败:', filePath, err);
}
});
}
// #endif
};
const confirmEdit = async () => {
if (!tempEditData.avatar) {
uni.showToast({
title: '请选择头像',
icon: 'none'
});
return;
}
if (!tempEditData.recipient.trim()) {
uni.showToast({
title: '请输入收款方',
icon: 'none'
});
return;
}
if (!tempEditData.paymentMethod.trim()) {
uni.showToast({
title: '请输入付款方式',
icon: 'none'
});
return;
}
// 处理头像持久化 (仅限 APP 端)
let finalAvatarPath = tempEditData.avatar;
// #ifdef APP-PLUS
if (tempEditData.avatar !== transferData.avatar) {
// 如果图片发生了变化,保存新图并尝试删除旧图
finalAvatarPath = await saveImageToLocal(tempEditData.avatar);
if (transferData.avatar) {
removeLocalFile(transferData.avatar);
}
}
// #endif
transferData.recipient = tempEditData.recipient;
transferData.paymentMethod = tempEditData.paymentMethod;
transferData.avatar = finalAvatarPath;
transferData.promoTitle = tempEditData.promoTitle;
transferData.promoTagDesc = tempEditData.promoTagDesc;
transferData.promoPrice = tempEditData.promoPrice;
// 保存到本地缓存
uni.setStorageSync(STORAGE_KEY, transferData);
closeEditPopup();
};
const chooseAvatar = () => {
uni.chooseImage({
count: 1,
sizeType: ['compressed'],
sourceType: ['album', 'camera'],
success: (res) => {
tempEditData.avatar = res.tempFilePaths[0];
}
});
};
onMounted(() => {
const systemInfo = uni.getSystemInfoSync();
if (systemInfo.statusBarHeight) {
statusBarHeight.value = systemInfo.statusBarHeight;
}
});
onShow(() => {
// #ifdef APP-PLUS
util.setAndroidSystemBarColor('#F5F5F5')
uni.setNavigationBarColor({
animation: { // 动画效果
duration: 100,
timingFunc: 'easeIn'
}
})
setTimeout(() => {
plus.navigator.setStatusBarStyle("light");
}, 500);
// #endif
})
const goBack = () => {
uni.navigateBack();
};
</script>
<style lang="less" scoped>
@import "@/common/main.css";
.container {
min-height: 100vh;
background-color: #F5F5F5;
}
/* 编辑弹窗样式 */
.edit-modal {
background-color: #fff;
width: 600rpx;
border-radius: 40rpx;
padding: 40rpx;
display: flex;
flex-direction: column;
align-items: center;
.modal-title {
font-size: 32rpx;
font-weight: 500;
color: #1a1a1a;
margin-bottom: 40rpx;
text-align: center;
}
.modal-content {
width: 100%;
overflow: hidden;
.modal-scroll {
max-height: 60vh;
}
.form-title-desc {
display: block;
color: #767676;
font-size: 24rpx;
padding-bottom: 24rpx;
}
.form-item {
display: flex;
align-items: center;
margin-bottom: 32rpx;
width: 100%;
::v-deep .uni-easyinput__content {
background-color: transparent !important;
}
.require-dot {
color: #ff4d4f;
margin-right: 10rpx;
font-size: 32rpx;
}
.label {
font-size: 28rpx;
color: #1A1A1A;
width: 160rpx;
flex-shrink: 0;
}
.avatar-picker {
width: 84rpx;
height: 84rpx;
background-color: #f5f5f5;
border-radius: 12rpx;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
image {
width: 100%;
height: 100%;
}
}
.input-with-unit {
flex: 1;
display: flex;
align-items: center;
background-color: #F6F6F6;
border-radius: 12rpx;
padding: 0rpx 20rpx;
.input {
flex: 1;
background-color: transparent;
padding: 0;
font-size: 28rpx;
::v-deep .uni-easyinput__content-input {
height: 70rpx !important;
}
::v-deep .uni-easyinput__placeholder-class {
font-size: 28rpx !important;
}
}
.unit {
font-size: 28rpx;
color: #1A1A1A;
margin-left: 10rpx;
}
}
.input {
flex: 1;
background-color: #F6F6F6;
border-radius: 12rpx;
padding: 0rpx 20rpx;
font-size: 28rpx;
::v-deep .uni-easyinput__content-input {
height: 70rpx !important;
}
::v-deep .uni-easyinput__placeholder-class {
font-size: 28rpx !important;
}
}
}
}
.modal-footer {
display: flex;
justify-content: space-between;
width: 100%;
margin-top: 40rpx;
.btn {
width: 240rpx;
height: 88rpx;
line-height: 88rpx;
text-align: center;
border-radius: 12rpx;
font-size: 32rpx;
font-weight: 500;
}
.cancel {
background-color: #F1F1F1;
color: #767676;
}
.confirm {
background-color: #1777FF;
color: #fff;
}
}
}
.header-section {
background: linear-gradient(181deg, #3884FD 60%, rgba(30, 128, 253, 0.5) 81.75%, rgba(30, 128, 253, 0) 100%);
::v-deep .nav-bar-container {
position: relative !important;
}
::v-deep .placeholder-box {
display: none !important;
background: transparent;
}
::v-deep .tipLayer {
position: absolute;
}
}
.status-bar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 40rpx;
}
.nav-link {
padding: 0 30rpx;
text-align: right;
font-size: 28rpx;
line-height: 28rpx;
color: #fff;
}
.success-info {
display: flex;
flex-direction: column;
align-items: center;
margin-top: 20rpx;
}
.status-box {
display: flex;
align-items: center;
margin-top: 10rpx;
height: 34rpx;
.image {
width: 34rpx;
height: 34rpx;
margin-right: 12rpx;
}
.status-text {
font-size: 34rpx;
color: #FFFFFF;
font-weight: 500;
line-height: 34rpx;
}
}
.amount-box {
display: flex;
align-items: baseline;
color: #fff;
margin-top: 40rpx;
margin-bottom: 72rpx;
.symbol {
font-size: 68rpx;
margin-right: 4rpx;
}
.amount {
font-size: 92rpx;
line-height: 92rpx;
font-weight: 500;
}
}
.details-box {
width: 100%;
padding: 0 46rpx;
.detail-item {
display: flex;
justify-content: space-between;
margin-bottom: 20rpx;
height: 26rpx;
font-size: 26rpx;
color: rgba(255, 255, 255, 0.9);
text {
font-size: 26rpx;
color: rgba(255, 255, 255, 0.9);
line-height: 26rpx;
}
}
}
.content-section {
padding: 24rpx;
background-color: #F5F5F5;
border-radius: 30rpx 30rpx 0 0;
}
.card {
background-color: #fff;
border-radius: 20rpx;
margin-bottom: 24rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.02);
}
.notification-card {
padding: 24rpx 16rpx;
margin: 10rpx 24rpx;
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 22rpx;
.card-title {
font-size: 28rpx;
line-height: 28rpx;
font-weight: 500;
color: #1A1A1A;
}
.card-sub-title {
font-size: 28rpx;
line-height: 28rpx;
color: #969696;
display: flex;
align-items: center;
}
}
.card-item-bg {
background-color: #fff;
background-image: url("/static/image/balance/transfer/card-bg.png");
background-size: cover;
background-position: center;
border-radius: 16rpx;
padding: 18rpx;
display: flex;
justify-content: space-between;
align-items: center;
position: relative;
overflow: hidden;
.user-info {
display: flex;
align-items: center;
z-index: 1;
.avatar {
width: 100rpx;
height: 100rpx;
border-radius: 8rpx;
margin-right: 22rpx;
border: 2px solid #FFFFFF;
}
.name-box {
display: flex;
align-items: center;
margin-bottom: 10rpx;
.name {
font-size: 32rpx;
line-height: 32rpx;
color: #35363B;
font-weight: 500;
}
.remark-link {
font-size: 26rpx;
line-height: 26rpx;
color: #1777FF;
margin-left: 20rpx;
}
}
.desc {
font-size: 26rpx;
line-height: 26rpx;
color: #969696;
}
}
.notify-btn {
height: 52rpx;
line-height: 52rpx;
padding: 0 20rpx;
background-color: #1777FF;
color: #FFFFFF;
font-size: 24rpx;
border-radius: 26rpx;
z-index: 1;
}
.watermark-icon {
position: absolute;
right: 20rpx;
bottom: -20rpx;
opacity: 0.5;
}
}
}
.promo-card {
// padding: 30rpx;
margin: 24rpx;
margin-bottom: 10rpx;
display: flex;
justify-content: space-between;
align-items: center;
.promo-left-box {
display: flex;
align-items: center;
padding: 34rpx;
background-image: url("/static/image/balance/transfer/card-bg-2-left.png");
background-repeat: no-repeat;
background-size: 100% 100%;
background-position: center;
height: 144rpx;
.img {
width: 70rpx;
height: 70rpx;
}
}
.promo-right {
height: 144rpx;
background-image: url("/static/image/balance/transfer/card-bg-2-right.png");
background-repeat: no-repeat;
background-size: 100% 100%;
background-position: center;
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
padding: 32rpx 32rpx 24rpx;
.promo-title {
font-size: 28rpx;
line-height: 28rpx;
font-weight: 500;
color: #1A1A1A;
}
.promo-tags {
display: flex;
align-items: center;
margin-top: 8rpx;
.tag {
font-size: 24rpx;
line-height: 34rpx;
height: 34rpx;
color: #EF673C;
background-color: #FFF2F1;
border-radius: 4rpx;
margin-right: 12rpx;
padding: 0 12rpx;
}
.tag-desc {
font-size: 24rpx;
line-height: 24rpx;
color: #969696;
}
}
}
.promo-right-box {
display: flex;
flex-direction: column;
align-items: center;
.price-box {
color: #F93950;
.p-num {
font-size: 34rpx;
line-height: 24rpx;
font-weight: 500;
}
.p-unit {
font-size: 24rpx;
line-height: 24rpx;
margin-left: 2rpx;
}
}
.free-btn {
height: 52rpx;
line-height: 52rpx;
padding: 0 24rpx;
background-color: #FA3A49;
color: #fff;
font-size: 24rpx;
border-radius: 26rpx;
}
}
}
.fliggy-card {
overflow: hidden;
.banner-img {
width: 100%;
}
.yaoyiyao-container {
width: 100%;
display: flex;
justify-content: center;
margin-top: -80rpx;
padding-bottom: 16rpx;
.yaoyiyao-box {
padding: 12rpx 18rpx;
display: flex;
align-items: center;
background-color: #000000;
border-radius: 28rpx;
opacity: 0.5;
.img {
width: 32rpx;
height: 32rpx;
margin-right: 14rpx;
}
.text {
color: #FFFFFF;
font-size: 24rpx;
line-height: 24rpx;
}
}
}
.banner-footer {
padding: 24rpx;
display: flex;
justify-content: space-between;
align-items: center;
.banner-info {
display: flex;
align-items: center;
.logo-box {
width: 76rpx;
height: 76rpx;
display: flex;
align-items: center;
justify-content: center;
margin-right: 20rpx;
.logo {
width: 76rpx;
height: 76rpx;
}
}
.text-content {
display: flex;
flex-direction: column;
}
.b-title {
font-size: 28rpx;
line-height: 28rpx;
color: #1A1A1A;
font-weight: 500;
}
.b-desc {
margin-top: 12rpx;
font-size: 24rpx;
line-height: 24rpx;
color: #969696;
}
}
.b-btn {
padding: 0 20rpx;
background-color: #FF3A31;
color: #fff;
font-size: 26rpx;
height: 52rpx;
line-height: 52rpx;
border-radius: 8rpx;
}
}
}
.footer-section {
padding: 8rpx 40rpx 0;
display: flex;
justify-content: center;
padding-bottom: 24rpx;
padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
padding-bottom: calc(24rpx + constant(safe-area-inset-bottom));
.finish-btn {
height: 84rpx;
width: 390rpx;
line-height: 84rpx;
text-align: center;
border: 1px solid #2172CA;
border-radius: 42rpx;
color: #2172CA;
font-size: 32rpx;
background-color: transparent;
}
}
</style>