This commit is contained in:
@@ -41,6 +41,11 @@
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue';
|
||||
import { authApi } from '@/api/index.js';
|
||||
import { useUserStore } from '@/store/index.js';
|
||||
|
||||
const POST_BIND_RELOGIN_NOTICE_KEY = 'postBindReloginNotice';
|
||||
|
||||
const userStore = useUserStore();
|
||||
|
||||
let bind = reactive({
|
||||
phone: '',
|
||||
@@ -60,6 +65,23 @@
|
||||
fromLogin.value = options.fromLogin === 'true';
|
||||
});
|
||||
|
||||
const prepareManualRelogin = () => {
|
||||
const preservedIdentifier = userStore.state.identifier || uni.getStorageSync('identifier') || '';
|
||||
|
||||
userStore.clearUser();
|
||||
if (preservedIdentifier) {
|
||||
userStore.setIdentifier(preservedIdentifier);
|
||||
}
|
||||
|
||||
uni.setStorageSync(POST_BIND_RELOGIN_NOTICE_KEY, '1');
|
||||
|
||||
// #ifdef H5
|
||||
if (typeof sessionStorage !== 'undefined') {
|
||||
sessionStorage.removeItem('assetToken');
|
||||
}
|
||||
// #endif
|
||||
};
|
||||
|
||||
const getCode = async () => {
|
||||
if (!bind.phone) {
|
||||
uni.showToast({ title: '请输入手机号', icon: 'none' });
|
||||
@@ -100,15 +122,17 @@
|
||||
|
||||
try {
|
||||
await authApi.bindPhone(bind.phone, bind.code);
|
||||
|
||||
if (fromLogin.value) {
|
||||
prepareManualRelogin();
|
||||
uni.reLaunch({ url: '/pages/login/login' });
|
||||
return;
|
||||
}
|
||||
|
||||
uni.showToast({ title: '绑定成功', icon: 'success' });
|
||||
setTimeout(() => {
|
||||
if (fromLogin.value) {
|
||||
// 从登录页面过来的,绑定成功后跳转到首页
|
||||
uni.redirectTo({ url: '/pages/index/index' });
|
||||
} else {
|
||||
// 从其他页面过来的,返回上一页
|
||||
uni.navigateBack();
|
||||
}
|
||||
// 从其他页面过来的,返回上一页
|
||||
uni.navigateBack();
|
||||
}, 1500);
|
||||
} catch (e) {
|
||||
console.error('绑定手机号失败', e);
|
||||
@@ -193,4 +217,4 @@
|
||||
.container {
|
||||
padding: var(--space-md);
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -113,6 +113,7 @@
|
||||
onMounted,
|
||||
computed
|
||||
} from 'vue';
|
||||
import { onShow } from '@dcloudio/uni-app';
|
||||
import UserInfoCard from '@/components/UserInfoCard.vue';
|
||||
import DeviceStatusCard from '@/components/DeviceStatusCard.vue';
|
||||
import VoiceCard from '@/components/VoiceCard.vue';
|
||||
@@ -207,6 +208,7 @@
|
||||
let showSmsCode = ref(false);
|
||||
let smsCodePhone = ref('');
|
||||
let smsCode = ref('');
|
||||
let indexEntryChecking = ref(false);
|
||||
|
||||
const formatDate = (dateStr) => {
|
||||
if (!dateStr) return '-';
|
||||
@@ -293,10 +295,33 @@
|
||||
}
|
||||
|
||||
// 到期时间由 TrafficCard 组件获取套餐信息时一并返回
|
||||
return data;
|
||||
} catch (e) {
|
||||
console.error('加载资产信息失败', e);
|
||||
return null;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleIndexEntry = async () => {
|
||||
const token = uni.getStorageSync('token');
|
||||
if (!token) {
|
||||
uni.reLaunch({ url: '/pages/login/login' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (indexEntryChecking.value) return;
|
||||
indexEntryChecking.value = true;
|
||||
|
||||
try {
|
||||
const data = await loadAssetInfo();
|
||||
if (data && !data.bound_phone) {
|
||||
uni.redirectTo({ url: '/pages/bind/bind?fromLogin=true' });
|
||||
}
|
||||
} finally {
|
||||
indexEntryChecking.value = false;
|
||||
}
|
||||
loading.value = false;
|
||||
};
|
||||
|
||||
// 处理套餐加载完成事件(从 TrafficCard 组件传递过来)
|
||||
@@ -736,13 +761,11 @@
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
const token = uni.getStorageSync('token');
|
||||
if (!token) {
|
||||
uni.reLaunch({ url: '/pages/login/login' });
|
||||
return;
|
||||
}
|
||||
initCurrentMonth();
|
||||
loadAssetInfo();
|
||||
});
|
||||
|
||||
onShow(() => {
|
||||
handleIndexEntry();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -70,13 +70,24 @@
|
||||
import { useUserStore } from '@/store/index.js';
|
||||
import { initWxConfig, wxScan, isInWechat } from '@/utils/wxsdk.js';
|
||||
|
||||
const POST_BIND_RELOGIN_NOTICE_KEY = 'postBindReloginNotice';
|
||||
|
||||
const userStore = useUserStore();
|
||||
const identifier = ref(uni.getStorageSync('identifier') || '');
|
||||
const loading = ref(false);
|
||||
const agreed = ref(true);
|
||||
const inputFocus = ref(false);
|
||||
const showError = ref(false);
|
||||
const wechatAppId = ref('');
|
||||
|
||||
onMounted(async () => {
|
||||
const token = uni.getStorageSync('token');
|
||||
if (token) {
|
||||
uni.reLaunch({ url: '/pages/index/index' });
|
||||
return;
|
||||
}
|
||||
|
||||
showPostBindReloginNotice();
|
||||
handleWechatCallback();
|
||||
getPathDeviceId();
|
||||
|
||||
@@ -93,12 +104,18 @@
|
||||
// #endif
|
||||
});
|
||||
|
||||
const identifier = ref('');
|
||||
const loading = ref(false);
|
||||
const agreed = ref(true);
|
||||
const inputFocus = ref(false);
|
||||
const showError = ref(false);
|
||||
const wechatAppId = ref('');
|
||||
const showPostBindReloginNotice = () => {
|
||||
if (uni.getStorageSync(POST_BIND_RELOGIN_NOTICE_KEY) !== '1') {
|
||||
return;
|
||||
}
|
||||
|
||||
uni.removeStorageSync(POST_BIND_RELOGIN_NOTICE_KEY);
|
||||
uni.showToast({
|
||||
title: '绑定手机号成功,请重新登录',
|
||||
icon: 'none',
|
||||
duration: 2500
|
||||
});
|
||||
};
|
||||
|
||||
const getCode = () => {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
@@ -152,7 +169,7 @@
|
||||
doLogin();
|
||||
};
|
||||
|
||||
const handleScanLogin = async () => {
|
||||
const handleScanLogin = async () => {
|
||||
// #ifdef H5
|
||||
try {
|
||||
// 检查是否在微信环境
|
||||
@@ -218,7 +235,6 @@ const handleScanLogin = async () => {
|
||||
};
|
||||
|
||||
const handleWechatCallback = async () => {
|
||||
|
||||
const code = getCode();
|
||||
const assetToken = sessionStorage.getItem('assetToken');
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
<view class="info-label">支付方式</view>
|
||||
<view class="info-value">{{ item.payment_method || '-' }}</view>
|
||||
</view>
|
||||
<view class="info-row flex-row-sb">
|
||||
<view v-if="hasAutoPurchaseStatus(item)" class="info-row flex-row-sb">
|
||||
<view class="info-label">自动购包</view>
|
||||
<view class="info-value">
|
||||
{{ getAutoPurchaseStatusText(item.auto_purchase_status) }}</view>
|
||||
@@ -64,7 +64,9 @@
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="item.status === 0" class="record-actions">
|
||||
<button class="btn-pay" @tap="handleRechargePayment(item)">立即支付</button>
|
||||
<button class="btn-pay" :disabled="rechargeOrderSubmittingKey !== null" @tap="handleRechargePayment(item)">
|
||||
{{ isRechargeOrderSubmitting(item) ? '处理中...' : '立即支付' }}
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -93,10 +95,6 @@
|
||||
</view>
|
||||
</view>
|
||||
<view class="record-info">
|
||||
<view class="info-row flex-row-sb">
|
||||
<view class="info-label">流水ID</view>
|
||||
<view class="info-value text-ellipsis">{{ item.transaction_id }}</view>
|
||||
</view>
|
||||
<view class="info-row flex-row-sb">
|
||||
<view class="info-label">变动后余额</view>
|
||||
<view class="info-value">¥{{ formatMoney(item.balance_after) }}</view>
|
||||
@@ -106,7 +104,7 @@
|
||||
<view class="info-value">{{ item.remark }}</view>
|
||||
</view>
|
||||
<view class="info-row flex-row-sb">
|
||||
<view class="info-label">创建时间</view>
|
||||
<view class="info-label">支付时间</view>
|
||||
<view class="info-value">{{ item.created_at }}</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -143,12 +141,14 @@
|
||||
|
||||
<view class="custom-input" v-if="isCustomAmount">
|
||||
<view class="input-label">请输入充值金额(元)</view>
|
||||
<up-input v-model="customAmount" type="number" placeholder="最低0.01元" border="surround" />
|
||||
<up-input v-model="customAmount" type="number" placeholder="最低1元" border="surround" />
|
||||
</view>
|
||||
|
||||
<view class="popup-footer">
|
||||
<button class="btn-apple btn-secondary" @tap="showRechargeModal=false">取消</button>
|
||||
<button class="btn-apple btn-primary" @tap="confirmRecharge">确认充值</button>
|
||||
<button class="btn-apple btn-primary" :disabled="rechargeSubmitting" @tap="confirmRecharge">
|
||||
{{ rechargeSubmitting ? '处理中...' : '确认充值' }}
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</up-popup>
|
||||
@@ -228,6 +228,8 @@
|
||||
let selectedAmount = ref(null);
|
||||
let isCustomAmount = ref(false);
|
||||
let customAmount = ref('');
|
||||
let rechargeSubmitting = ref(false);
|
||||
let rechargeOrderSubmittingKey = ref(null);
|
||||
const rechargeAmounts = [{
|
||||
value: 1000,
|
||||
label: '10'
|
||||
@@ -266,13 +268,17 @@
|
||||
};
|
||||
|
||||
const confirmRecharge = async () => {
|
||||
if (rechargeSubmitting.value) return;
|
||||
rechargeSubmitting.value = true;
|
||||
|
||||
let amount = 0;
|
||||
|
||||
if (isCustomAmount.value) {
|
||||
// 自定义金额
|
||||
const customAmountNum = parseFloat(customAmount.value);
|
||||
if (!customAmountNum || customAmountNum < 0.01) {
|
||||
if (!customAmountNum || customAmountNum < 1) {
|
||||
showRechargeModal.value = false;
|
||||
rechargeSubmitting.value = false;
|
||||
setTimeout(() => {
|
||||
uni.showToast({
|
||||
title: '请输入正确的充值金额',
|
||||
@@ -287,6 +293,7 @@
|
||||
amount = selectedAmount.value;
|
||||
} else {
|
||||
showRechargeModal.value = false;
|
||||
rechargeSubmitting.value = false;
|
||||
setTimeout(() => {
|
||||
uni.showToast({
|
||||
title: '请选择充值金额',
|
||||
@@ -378,6 +385,8 @@
|
||||
icon: 'none'
|
||||
});
|
||||
}, 50);
|
||||
} finally {
|
||||
rechargeSubmitting.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -402,6 +411,20 @@
|
||||
return statusMap[status] || '未开启';
|
||||
};
|
||||
|
||||
const hasAutoPurchaseStatus = (item) => Object.prototype.hasOwnProperty.call(item, 'auto_purchase_status') &&
|
||||
item.auto_purchase_status !== null &&
|
||||
item.auto_purchase_status !== undefined &&
|
||||
item.auto_purchase_status !== '';
|
||||
|
||||
const getRechargeOrderSubmitKey = (rechargeOrder) => rechargeOrder.recharge_order_no ||
|
||||
rechargeOrder.recharge_no ||
|
||||
rechargeOrder.recharge_id ||
|
||||
rechargeOrder.order_id ||
|
||||
`${rechargeOrder.amount}-${rechargeOrder.created_at}`;
|
||||
|
||||
const isRechargeOrderSubmitting = (rechargeOrder) => rechargeOrderSubmittingKey.value ===
|
||||
getRechargeOrderSubmitKey(rechargeOrder);
|
||||
|
||||
const loadWalletDetail = async () => {
|
||||
try {
|
||||
const data = await walletApi.getDetail(userStore.state.identifier);
|
||||
@@ -480,6 +503,9 @@
|
||||
};
|
||||
|
||||
const handleRechargePayment = async (rechargeOrder) => {
|
||||
if (rechargeOrderSubmittingKey.value !== null) return;
|
||||
|
||||
rechargeOrderSubmittingKey.value = getRechargeOrderSubmitKey(rechargeOrder);
|
||||
uni.showLoading({
|
||||
title: '处理中...',
|
||||
mask: true
|
||||
@@ -528,6 +554,8 @@
|
||||
title: errorMsg,
|
||||
icon: 'none'
|
||||
});
|
||||
} finally {
|
||||
rechargeOrderSubmittingKey.value = null;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -787,9 +815,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
.record-actions {
|
||||
margin-top: 20rpx;
|
||||
padding-top: 20rpx;
|
||||
.record-actions {
|
||||
margin-top: 20rpx;
|
||||
padding-top: 20rpx;
|
||||
border-top: 1rpx solid var(--border-light);
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
@@ -800,12 +828,16 @@
|
||||
border: none;
|
||||
border-radius: 8rpx;
|
||||
padding: 12rpx 32rpx;
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
width:100%;
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
width:100%;
|
||||
|
||||
&::after {
|
||||
border: none;
|
||||
&[disabled] {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
&::after {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -931,6 +963,10 @@
|
||||
font-weight: 500;
|
||||
border: none;
|
||||
|
||||
&[disabled] {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
&.btn-secondary {
|
||||
background: var(--gray-200);
|
||||
color: var(--text-primary);
|
||||
@@ -943,4 +979,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -62,7 +62,9 @@
|
||||
</view> -->
|
||||
|
||||
<view v-if="item.payment_status === 1" class="card-footer">
|
||||
<button class="btn-pay" @tap="handleOrderPayment(item)">立即支付</button>
|
||||
<button class="btn-pay" :disabled="orderPayingId !== null" @tap="handleOrderPayment(item)">
|
||||
{{ isOrderPaying(item) ? '处理中...' : '立即支付' }}
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -97,6 +99,7 @@
|
||||
let loading = ref(false);
|
||||
let noMore = ref(false);
|
||||
let page = ref(1);
|
||||
let orderPayingId = ref(null);
|
||||
const pageSize = 10;
|
||||
let filterIndex = ref(0);
|
||||
let filterOptions = [{
|
||||
@@ -188,7 +191,12 @@
|
||||
}
|
||||
};
|
||||
|
||||
const isOrderPaying = (order) => orderPayingId.value === order.order_id;
|
||||
|
||||
const handleOrderPayment = async (order) => {
|
||||
if (orderPayingId.value !== null || !order?.order_id) return;
|
||||
orderPayingId.value = order.order_id;
|
||||
|
||||
uni.showLoading({
|
||||
title: '处理中...',
|
||||
mask: true
|
||||
@@ -230,6 +238,8 @@
|
||||
title: errorMsg,
|
||||
icon: 'none'
|
||||
});
|
||||
} finally {
|
||||
orderPayingId.value = null;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -445,6 +455,10 @@
|
||||
font-weight: 500;
|
||||
width:100%;
|
||||
|
||||
&[disabled] {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
&::after {
|
||||
border: none;
|
||||
}
|
||||
@@ -486,4 +500,4 @@
|
||||
color: #999;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -70,7 +70,9 @@
|
||||
|
||||
<view class="popup-footer">
|
||||
<button class="btn-apple btn-secondary" @tap="showModal=false">取消</button>
|
||||
<button class="btn-apple btn-primary" @tap="confirmPay">确认支付</button>
|
||||
<button class="btn-apple btn-primary" :disabled="paySubmitting" @tap="confirmPay">
|
||||
{{ paySubmitting ? '处理中...' : '确认支付' }}
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</up-popup>
|
||||
@@ -91,6 +93,7 @@
|
||||
let loading = ref(false);
|
||||
let paymentMethod = ref('wechat');
|
||||
let walletBalance = ref(0);
|
||||
let paySubmitting = ref(false);
|
||||
|
||||
const formatMoney = (amount) => {
|
||||
if (!amount && amount !== 0) return '0.00';
|
||||
@@ -135,7 +138,9 @@
|
||||
};
|
||||
|
||||
const confirmPay = async () => {
|
||||
if (!currentPackage.value) return;
|
||||
if (paySubmitting.value || !currentPackage.value) return;
|
||||
paySubmitting.value = true;
|
||||
let releaseInModalCallback = false;
|
||||
|
||||
uni.showLoading({ title: '创建订单...', mask: true });
|
||||
|
||||
@@ -200,6 +205,7 @@
|
||||
// 提示用户需要强充
|
||||
const packageNameStr = packageNames.length > 0 ? packageNames.join('、') : '套餐';
|
||||
const message = `购买${packageNameStr}需要先充值 ¥${formatMoney(forceRechargeAmount)},充值成功后将自动购买套餐`;
|
||||
releaseInModalCallback = true;
|
||||
|
||||
uni.showModal({
|
||||
title: '需要充值',
|
||||
@@ -207,10 +213,17 @@
|
||||
confirmText: '去充值',
|
||||
cancelText: '取消',
|
||||
success: async (res) => {
|
||||
if (res.confirm) {
|
||||
// 调起微信支付
|
||||
await handleWechatPay(orderResult.pay_config, true);
|
||||
try {
|
||||
if (res.confirm) {
|
||||
// 调起微信支付
|
||||
await handleWechatPay(orderResult.pay_config, true);
|
||||
}
|
||||
} finally {
|
||||
paySubmitting.value = false;
|
||||
}
|
||||
},
|
||||
fail: () => {
|
||||
paySubmitting.value = false;
|
||||
}
|
||||
});
|
||||
return;
|
||||
@@ -273,6 +286,10 @@
|
||||
// 其他错误提示
|
||||
const errorMsg = e.msg || e.message || '操作失败,请稍后重试';
|
||||
uni.showToast({ title: errorMsg, icon: 'none' });
|
||||
} finally {
|
||||
if (!releaseInModalCallback) {
|
||||
paySubmitting.value = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -515,20 +532,24 @@
|
||||
padding: 30rpx;
|
||||
border-top: 1rpx solid var(--border-light);
|
||||
|
||||
.btn-apple {
|
||||
flex: 1;
|
||||
height: 88rpx;
|
||||
display: flex;
|
||||
.btn-apple {
|
||||
flex: 1;
|
||||
height: 88rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: var(--radius-medium);
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
border: none;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
border: none;
|
||||
|
||||
&.btn-secondary {
|
||||
background: var(--gray-200);
|
||||
color: var(--text-primary);
|
||||
&[disabled] {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
&.btn-secondary {
|
||||
background: var(--gray-200);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
&.btn-primary {
|
||||
@@ -538,4 +559,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user