fix:修复bug

This commit is contained in:
2026-03-17 12:03:54 +08:00
parent 5d7b973ddd
commit 60df817de5
32 changed files with 654 additions and 528 deletions
+133 -154
View File
@@ -1,117 +1,100 @@
<script setup>
import * as R from "ramda";
const { t } = useI18n();
// @ts-ignore
import { debounce } from "throttle-debounce";
// @ts-ignore
import Config from "@/config";
const isSubmit = ref(false)
function submit() {
isSubmit.value = true
setTimeout(() => {
isSubmit.value = false
}, 3000)
}
// 提交
const handleSubmit = debounce(Config.debounceLongTime, submit, {
atBegin: true
})
</script>
<script>
// @ts-ignore
import { debounce } from "throttle-debounce";
// @ts-ignore
import Config from "@/config";
import { appUserCardSavePost } from "@/service";
export default {
data() {
return {
isSubmit: false
}
},
onLoad() {
console.log('onLoad')
// @ts-ignore
this.handleSuccess = debounce(Config.debounceLongTime, this.handleSuccess, {
atBegin: true
})
},
methods: {
handleError() {
uni.showToast({
icon: 'none',
title: 'Add credit card failed'
import * as R from "ramda";
const { t } = useI18n();
// @ts-ignore
import {debounce} from "throttle-debounce";
// @ts-ignore
import Config from "@/config";
const isSubmit = ref(false)
function submit() {
isSubmit.value = true
setTimeout(() => {
isSubmit.value = false
}, 3000)
}
// 提交
const handleSubmit = debounce(Config.debounceLongTime, submit, {
atBegin: true
})
</script>
<script>
// @ts-ignore
import {debounce} from "throttle-debounce";
// @ts-ignore
import Config from "@/config";
import {appUserCardSavePost} from "@/service";
export default {
data() {
return {
isSubmit: false
}
},
onLoad() {
console.log('onLoad')
// @ts-ignore
this.handleSuccess = debounce(Config.debounceLongTime, this.handleSuccess, {
atBegin: true
})
},
async handleSuccess(data) {
try {
const params = {
cardNumber: '************' + data.card.last4,
cardId: data.id,
}
const res = await appUserCardSavePost({
body: params
methods: {
handleError() {
uni.showToast({
icon: 'none',
title: 'Add credit card failed'
})
console.log('handleSuccess', res)
if (uni.getStorageSync('UNI_LOCALE') == 'zh-Hans') {
await uni.showToast({
icon: 'none',
title: '信用卡添加成功'
},
async handleSuccess(data) {
try {
const params = {
cardNumber: '************' + data.card.last4,
cardId: data.id,
}
const res = await appUserCardSavePost({
body: params
})
} else {
console.log('handleSuccess', res)
await uni.showToast({
icon: 'none',
title: 'The credit card was added successfully.'
})
// const eventChannel = this.getOpenerEventChannel();
// const id = res.data
// eventChannel.emit('acceptDataFromOpenedPage', {...params, id});
setTimeout(uni.navigateBack, 1000)
} catch (e) {
}
// const eventChannel = this.getOpenerEventChannel();
// const id = res.data
// eventChannel.emit('acceptDataFromOpenedPage', {...params, id});
setTimeout(uni.navigateBack, 1000)
} catch (e) {
uni.showToast({
icon: 'none',
title: e?.message || e?.data?.msg || 'Add credit card failed'
})
},
}
}
</script>
<script module="addRenderjs" lang="renderjs">
import {loadStripe} from '@stripe/stripe-js/pure';
import Config from '@/config/index'
// @ts-ignore
export default {
data(){
return {
isCompleted: false,
stripe:null,
elements:null,
}
},
}
}
</script>
<script module="addRenderjs" lang="renderjs">
import {loadStripe} from '@stripe/stripe-js/pure';
import Config from '@/config/index'
// @ts-ignore
export default {
data(){
return {
isCompleted: false,
stripe:null,
elements:null,
}
},
mounted() {
console.log('mounted')
const locale = uni.getStorageSync('UNI_LOCALE') || 'en'
const loadingText = locale === 'zh-Hans' ? '加载中...' : 'Loading...'
uni.showLoading({
title: loadingText,
mask: true
})
this.init()
},
methods: {
async init(){
try {
mounted() {
console.log('mounted')
this.init()
},
methods: {
async init(){
console.log('本次初始化使用的key', Config.stripeKey)
loadStripe.setLoadParameters({advancedFraudSignals: false});
const stripe = await loadStripe(Config.stripeKey);
@@ -131,59 +114,55 @@ export default {
// enable payment button
}
});
uni.hideLoading()
} catch (error) {
console.error('初始化失败', error)
uni.hideLoading()
const locale = uni.getStorageSync('UNI_LOCALE') || 'en'
const errorText = locale === 'zh-Hans' ? '加载失败,请重试' : 'Loading failed, please try again'
uni.showToast({
icon: 'none',
title: errorText
})
}
},
async handleSubmit(isSubmit){
console.log('handleSubmit',isSubmit,this.isCompleted)
console.log('handleSubmit',this.stripe)
if(!isSubmit||!this.isCompleted||!this.stripe){
return
}
const {error, paymentMethod} = await this.stripe.createPaymentMethod({
elements:this.elements,
});
console.log(error)
console.log(paymentMethod)
if (error) {
// Handle error
this.$ownerInstance.callMethod('handleError')
} else {
// Send paymentMethod.id to your server
this.$ownerInstance.callMethod('handleSuccess',paymentMethod)
},
async handleSubmit(isSubmit){
console.log('handleSubmit',isSubmit,this.isCompleted)
console.log('handleSubmit',this.stripe)
if(!isSubmit||!this.isCompleted||!this.stripe){
return
}
const {error, paymentMethod} = await this.stripe.createPaymentMethod({
elements:this.elements,
});
console.log(error)
console.log(paymentMethod)
if (error) {
// Handle error
this.$ownerInstance.callMethod('handleError')
} else {
// Send paymentMethod.id to your server
this.$ownerInstance.callMethod('handleSuccess',paymentMethod)
}
}
}
}
}
</script>
<template>
<view class="">
<navbar customClass="!bg-transparent" />
<view class="text-center px-30rpx mt-98rpx">
<view class="text-46rpx lh-46rpx text-#333 font-bold">{{ Config.appName }}</view>
<view class="text-32rpx text-#333 font-500 mt-70rpx mb-12rpx">{{ t('pages-user.card.title') }}</view>
<view class="text-28rpx lh-28rpx text-#999">{{ t('pages-user.card.desc') }}</view>
</script>
<template>
<view class="">
<navbar customClass="!bg-transparent" />
<view class="text-center px-30rpx mt-98rpx">
<view class="text-46rpx lh-46rpx text-#333 font-bold">{{ Config.appName }}</view>
<view class="text-32rpx text-#333 font-500 mt-70rpx mb-12rpx">{{ t('pages-user.card.title') }}</view>
<view class="text-28rpx lh-28rpx text-#999">{{ t('pages-user.card.desc') }}</view>
</view>
<view class="mt-188rpx px-30rpx py-40rpx bg-#fff" id="payment-form"
:prop="isSubmit"
:change:prop="addRenderjs.handleSubmit"
></view>
<!-- 底部确认按钮 -->
<fixed-bottom-large-btn
class="z-100"
fixed
:text="t('common.save')"
@click="handleSubmit"
/>
</view>
<view class="mt-188rpx px-30rpx py-40rpx bg-#fff" id="payment-form" :prop="isSubmit"
:change:prop="addRenderjs.handleSubmit"></view>
<!-- 底部确认按钮 -->
<fixed-bottom-large-btn class="z-100" fixed :text="t('common.save')" @click="handleSubmit" />
</view>
</template>
<style scoped lang="scss">
page {
background-color: #F5F5F5;
}
</style>
</template>
<style scoped lang="scss">
page {
background-color: #F5F5F5;
}
</style>
+47 -24
View File
@@ -1,25 +1,57 @@
<script setup lang="ts">
import {appCouponExchangeCouponPost, appCouponUserCouponListPost} from "@/service";
const { t } = useI18n();
const { t, locale } = useI18n();
import { throttle } from "throttle-debounce";
import Config from "@/config";
import {dayjs} from "@/plugin";
function getList(pageNum: number, pageSize: number) {
return new Promise(resolve => {
appCouponUserCouponListPost({
params: {
pageNum,
pageSize,
}
}).then(res => {
resolve(res)
})
})
function isEnLocale() {
return String(locale.value || '').toLowerCase().startsWith('en')
}
const {paging, loading, firstLoaded, dataList, queryList} = usePage(getList)
function formatMoney(value: unknown) {
const n = Number(value)
if (!Number.isFinite(n)) return '0.00'
return n.toFixed(2)
}
function formatCouponDetail(item: any) {
const type = Number(item?.snapshotType)
const discount = Number(item?.snapshotDiscount)
const minAmount = Number(item?.snapshotMinAmount)
// 1-折扣券(percentage), 2-满减券(amount off with minimum spend)
if (type === 2) {
const hasMin = Number.isFinite(minAmount) && minAmount > 0
const discountText = `$${formatMoney(discount)}`
if (hasMin) {
const minText = `$${formatMoney(minAmount)}`
return isEnLocale()
? `Min ${minText} · Off ${discountText}`
: `${minText}${discountText}`
}
return isEnLocale() ? `Off ${discountText}` : `立减${discountText}`
}
if (type === 1) {
const pct = Number.isFinite(discount) ? Number(discount * 100).toFixed(0) : '0'
return `${pct}% ${t('pages-store.store.discount')}`
}
return t('pages-store.store.discount')
}
function getList(pageNum: number, pageSize: number) {
return appCouponUserCouponListPost({
params: {
pageNum,
pageSize,
}
}) as any
}
const {paging, loading, firstLoaded, dataList, queryList} = usePage<any>(getList)
const keyword = ref<string>("");
@@ -120,23 +152,14 @@ function handleSubmit() {
{{ item.snapshotMerchantId ? t('pages-user.coupon.merchant-specific') : t('pages-user.coupon.all-merchants') }}
</view>
<view class="text-24rpx lh-32rpx text-#999 my-18rpx">
{{ t('pages-user.coupon.expiry-date') }}{{ dayjs(Number(item.snapshotValidEnd))
.format('MM:DD HH:mm:ss') }}
{{ dayjs(Number(item.snapshotValidEnd)).format('YYYY-MM-DD HH:mm') }}{{ isEnLocale() ? ' expires' : '到期' }}
</view>
<view class="text-28rpx lh-28rpx text-#333 flex items-center">
<image
class="w-36rpx h-36rpx shrink-0 mr-10rpx"
src="@img/chef/106.png"
></image>
<template v-if="item.snapshotType === 2">
<!-- 满减-->
{{ item.snapshotDiscount }}
</template>
<template v-else>
{{ Number(item.snapshotDiscount * 100).toFixed(0) }}%
</template>
{{ t('pages-store.store.discount') }}
{{ formatCouponDetail(item) }}
</view>
</view>
<view class="h-84rpx lh-84rpx text-center text-28rpx text-#fff">
+42 -12
View File
@@ -5,7 +5,7 @@ import {
appMerchantOrderCanUseCouponListMerchantIdGet
} from "@/service";
const { t } = useI18n();
const { t, locale } = useI18n();
import { throttle } from "throttle-debounce";
import Config from "@/config";
import {dayjs} from "@/plugin";
@@ -23,15 +23,51 @@ function getList(pageNum: number, pageSize: number) {
return new Promise(resolve => {
appMerchantOrderCanUseCouponListMerchantIdGet({
params: {
merchantId: props.id || ''
merchantId: props.id || '',
}
}).then(res => {
resolve({ rows: res.data })
resolve({ rows: res.data } as any)
})
})
}) as any
}
const {paging, loading, firstLoaded, dataList, queryList} = usePage(getList)
const {paging, loading, firstLoaded, dataList, queryList} = usePage<any>(getList)
function isEnLocale() {
return String(locale.value || '').toLowerCase().startsWith('en')
}
function formatMoney(value: unknown) {
const n = Number(value)
if (!Number.isFinite(n)) return '0.00'
return n.toFixed(2)
}
function formatCouponDetail(item: any) {
const type = Number(item?.snapshotType)
const discount = Number(item?.snapshotDiscount)
const minAmount = Number(item?.snapshotMinAmount)
// 1-折扣券(percentage), 2-满减券(amount off with minimum spend)
if (type === 2) {
const hasMin = Number.isFinite(minAmount) && minAmount > 0
const discountText = `$${formatMoney(discount)}`
if (hasMin) {
const minText = `$${formatMoney(minAmount)}`
return isEnLocale()
? `Min ${minText} · Off ${discountText}`
: `${minText}${discountText}`
}
return isEnLocale() ? `Off ${discountText}` : `立减${discountText}`
}
if (type === 1) {
const pct = Number.isFinite(discount) ? Number(discount * 100).toFixed(0) : '0'
return `${pct}% ${t('pages-store.store.discount')}`
}
return t('pages-store.store.discount')
}
function confirmCoupon(item: any) {
eventChannel.emit('selectedCoupon', item);
@@ -72,13 +108,7 @@ function confirmCoupon(item: any) {
class="w-36rpx h-36rpx shrink-0 mr-10rpx"
src="@img/chef/106.png"
></image>
<template v-if="item.snapshotType === 2">
<!-- 满减-->
{{ item.snapshotDiscount }}
</template>
<template v-else>
{{ Number(item.snapshotDiscount * 100).toFixed(0) }}%
</template>{{ t('pages-store.store.discount') }}
{{ formatCouponDetail(item) }}
</view>
</view>
<view @click="confirmCoupon(item)" class="h-84rpx lh-84rpx text-center text-28rpx text-#fff">
+1 -1
View File
@@ -69,7 +69,7 @@ function appMembershipRechargeItem() {
<template v-if="!userStore.userInfo.userMembershipVo">
<!-- <text class="ml-20rpx text-#CE7138">{{ membershipRechargeItem.trialWeeksDisplay || 0 }} {{ t('pages-user.member.weeks') }}</text>
<text class="ml-20rpx text-#CE7138">{{ t('pages-user.member.free') }}</text> -->
<text class="ml-20rpx text-#CE7138">Free for 7 days</text>
<!-- <text class="ml-20rpx text-#CE7138">Free for 7 days</text> -->
<!-- <image :src="membershipRechargeItem.image"></image> -->
</template>
</view>
+75 -71
View File
@@ -52,14 +52,22 @@
</template>
<script lang="ts" setup>
import Config from "@/config";
import {EventEnum} from "@/constant/enums";
import {useLogicStore} from "@/pages-user/store/logic";
import {useUserStore} from "@/store";
import { getCurrentInstance } from "vue";
const {t, locale} = useI18n();
const userStore = useUserStore()
const logicStore = useLogicStore()
let openerEventChannel: any = null
onLoad(() => {
// 统一通过 getOpenerEventChannel 获取导航传入的 eventChannel
const pages = getCurrentPages() as any[]
const page = pages[pages.length - 1]
openerEventChannel = page?.getOpenerEventChannel?.() || null
})
// 生命周期:清空地址列表
onMounted(() => {
logicStore.clearPlacesList()
@@ -99,7 +107,7 @@ const querySearch = computed(() => {
function handleClickLocation(item: any) {
console.log('item', item)
uni.$emit(EventEnum.CHOOSE_ADDRESS, item)
openerEventChannel?.emit?.('chooseAddress', item)
uni.navigateBack()
}
@@ -130,7 +138,7 @@ function handleUseLocation() {
},
})
} else {
uni.$emit(EventEnum.CHOOSE_ADDRESS, {
openerEventChannel?.emit?.('chooseAddress', {
displayName: userStore.location.location,
formattedAddress: userStore.location.formattedAddress || '',
location: {
@@ -192,8 +200,8 @@ function getCityName(latitude: number, longitude: number) {
latitude
};
// 发事件
uni.$emit(EventEnum.CHOOSE_ADDRESS, {
// 回传上一页(eventChannel
openerEventChannel?.emit?.('chooseAddress', {
displayName: cityName,
formattedAddress,
location: { lng: longitude, lat: latitude }
@@ -220,69 +228,65 @@ const mapDataComputed = computed(() => {
lat: userStore.location.latitude || -36.8485,
}
})
</script>
<script lang="ts">
import { defineComponent } from "vue";
import { useLogicStore } from "@/pages-user/store/logic";
// 监听来自 renderjs 的事件
onMounted(() => {
// 监听搜索结果
uni.$on('MAP_SEARCH_RESULT', (places: any) => {
console.log(places, '接收到的搜索结果')
if (places && places.length > 0) {
// 解析实际返回的数据结构
const parsedPlaces = places.map((placeArray: any) => {
const placeData = placeArray[0]?.[0];
if (!placeData) return null;
export default defineComponent({
methods: {
onMapSearchResult(places: any) {
const logicStore = useLogicStore()
console.log(places, '接收到的搜索结果')
const placeId = placeData[1];
const formattedAddress = placeData[8] || '';
const locationArray = placeData[11];
const displayNameArray = placeData[27];
if (places && places.length > 0) {
// 保持与原先一致的解析逻辑
const parsedPlaces = places
.map((placeArray: any) => {
const placeData = placeArray[0]?.[0]
if (!placeData) return null
return {
id: placeId,
displayName: displayNameArray?.[0] || formattedAddress,
formattedAddress: formattedAddress,
location: locationArray ? {
lat: locationArray[0],
lng: locationArray[1]
} : null
};
}).filter(Boolean);
const placeId = placeData[1]
const formattedAddress = placeData[8] || ''
const locationArray = placeData[11]
const displayNameArray = placeData[27]
console.log('解析后的地址列表', parsedPlaces);
logicStore.setPlacesList(parsedPlaces);
} else {
logicStore.setPlacesList([]);
}
})
return {
id: placeId,
displayName: displayNameArray?.[0] || formattedAddress,
formattedAddress: formattedAddress,
location: locationArray
? {
lat: locationArray[0],
lng: locationArray[1],
}
: null,
}
})
.filter(Boolean)
// 监听地图未加载提示
uni.$on('MAP_NOT_LOADED', () => {
uni.showToast({
title: 'Map is not loaded yet, please wait',
icon: 'none'
});
})
// 监听搜索超时提示
uni.$on('MAP_SEARCH_TIMEOUT', () => {
uni.showToast({
title: 'Search timeout, please try again',
icon: 'none'
});
})
// 监听搜索加载状态
uni.$on('MAP_SEARCH_LOADING', (isLoading: boolean) => {
// logicStore.searchLoading = isLoading;
})
})
onUnmounted(() => {
// 清理事件监听
uni.$off('MAP_SEARCH_RESULT')
uni.$off('MAP_NOT_LOADED')
uni.$off('MAP_SEARCH_TIMEOUT')
uni.$off('MAP_SEARCH_LOADING')
console.log('解析后的地址列表', parsedPlaces)
logicStore.setPlacesList(parsedPlaces)
} else {
logicStore.setPlacesList([])
}
},
onMapNotLoaded() {
uni.showToast({
title: 'Map is not loaded yet, please wait',
icon: 'none',
})
},
onMapSearchTimeout() {
uni.showToast({
title: 'Search timeout, please try again',
icon: 'none',
})
},
onMapSearchLoading(_isLoading: boolean) {
// 如需联动 loading,可在此处驱动 store
},
},
})
</script>
<script lang="renderjs" module="mapRenderjs">
@@ -377,24 +381,24 @@ export default {
async searchPlace({keyword}) {
console.log('搜索关键词', keyword);
if (!keyword) {
uni.$emit('MAP_SEARCH_RESULT', [])
this.$ownerInstance.callMethod('onMapSearchResult', [])
return;
}
if (!mapLoaded) {
console.log('地图未加载完成,无法搜索');
uni.$emit('MAP_NOT_LOADED');
this.$ownerInstance.callMethod('onMapNotLoaded');
return;
}
if (!map || !this.google) {
return
}
uni.$emit('MAP_SEARCH_LOADING', true);
this.$ownerInstance.callMethod('onMapSearchLoading', true);
let timeoutId;
try {
// 设置搜索超时
const timeoutPromise = new Promise((_, reject) => {
timeoutId = setTimeout(() => {
uni.$emit('MAP_SEARCH_LOADING', false);
this.$ownerInstance.callMethod('onMapSearchLoading', false);
reject(new Error('Search timeout'));
}, 10000); // 10 秒超时
});
@@ -430,16 +434,16 @@ export default {
});
console.log('序列化后的搜索结果', serializedPlaces);
uni.$emit('MAP_SEARCH_RESULT', serializedPlaces);
this.$ownerInstance.callMethod('onMapSearchResult', serializedPlaces);
} catch (e) {
console.log('搜索错误原因', e);
if (e.message === 'Search timeout') {
uni.$emit('MAP_SEARCH_TIMEOUT');
this.$ownerInstance.callMethod('onMapSearchTimeout');
}
uni.$emit('MAP_SEARCH_RESULT', []);
this.$ownerInstance.callMethod('onMapSearchResult', []);
} finally {
clearTimeout(timeoutId);
uni.$emit('MAP_SEARCH_LOADING', false);
this.$ownerInstance.callMethod('onMapSearchLoading', false);
}
}
},