This commit is contained in:
parent
9d8b85987b
commit
d61d8e67a1
23
api/auth.ts
23
api/auth.ts
|
|
@ -1,6 +1,6 @@
|
|||
import { APP_CONFIG } from '@/config';
|
||||
import { HttpStatusError, httpRequest, toFormUrlEncoded } from '@/utils/http';
|
||||
import { postForm } from '@/utils/request';
|
||||
import { get, postForm } from '@/utils/request';
|
||||
|
||||
export interface LoginResponse {
|
||||
code: number | string;
|
||||
|
|
@ -58,6 +58,8 @@ export interface BindMobileResponse {
|
|||
code: number | string;
|
||||
msg?: string;
|
||||
data?: {
|
||||
token?: string;
|
||||
expires_in?: number;
|
||||
code?: string;
|
||||
msg?: string;
|
||||
mobile?: string;
|
||||
|
|
@ -76,3 +78,22 @@ export const bindMobile = (encryptedData: string) => {
|
|||
platform: resolveLoginPlatform()
|
||||
});
|
||||
};
|
||||
|
||||
export interface OpenInfoResult {
|
||||
code: number | string;
|
||||
msg?: string;
|
||||
data?: {
|
||||
user?: {
|
||||
user_id?: number | string;
|
||||
user_name?: string;
|
||||
avatar?: string;
|
||||
};
|
||||
open?: {
|
||||
user_id?: number | string;
|
||||
open_id?: number | string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/** 当前登录用户信息;有 user_id 视为已绑定过手机号/账号资料 */
|
||||
export const fetchOpenInfo = () => get<OpenInfoResult>('user/v1/open.info');
|
||||
|
|
|
|||
|
|
@ -1,17 +1,20 @@
|
|||
// #ifdef MP-ALIPAY
|
||||
const APP_ID = '123019';
|
||||
// const APP_ID = '123019';
|
||||
const APP_ID = '12317';
|
||||
// #endif
|
||||
// #ifdef MP-WEIXIN
|
||||
const APP_ID = '123018';
|
||||
// const APP_ID = '123018';
|
||||
const APP_ID = '12316';
|
||||
// #endif
|
||||
// #ifndef MP-WEIXIN
|
||||
// #ifndef MP-ALIPAY
|
||||
const APP_ID = '123018';
|
||||
// const APP_ID = '123018';
|
||||
const APP_ID = '12316';
|
||||
// #endif
|
||||
// #endif
|
||||
|
||||
export const APP_CONFIG = {
|
||||
API_BASE_URL: 'https://dev.api.leapy.cn',
|
||||
API_BASE_URL: 'https://api.leapy.cn',
|
||||
APP_ID,
|
||||
RESOURCE_URL: 'https://resource.leapy.cn/staff/',
|
||||
PROPERTY_IMG_URL: 'https://resource.leapy.cn/',
|
||||
|
|
|
|||
|
|
@ -22,13 +22,14 @@
|
|||
</view>
|
||||
|
||||
<view v-if="records.length && (loading || finished)" class="records-footer">
|
||||
<wd-loadmore :state="loadMoreState" />
|
||||
<wd-loadmore v-if="loading" state="loading" />
|
||||
<text v-else class="records-finished">没有更多了</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue';
|
||||
import { ref } from 'vue';
|
||||
import { onLoad, onPullDownRefresh, onReachBottom } from '@dcloudio/uni-app';
|
||||
import { platformScoreRecord } from '@/api/cashier';
|
||||
import type { ScoreRecordItem } from '@/api/cashier';
|
||||
|
|
@ -42,7 +43,6 @@ const loading = ref(false);
|
|||
const finished = ref(false);
|
||||
|
||||
const emptyImage = `${APP_CONFIG.RESOURCE_URL}tip/message.png`;
|
||||
const loadMoreState = computed(() => (loading.value ? 'loading' : 'finished'));
|
||||
|
||||
const formatMoney = (value?: string | number) => {
|
||||
const amount = Number(value || 0);
|
||||
|
|
@ -198,4 +198,12 @@ onReachBottom(() => {
|
|||
.records-footer {
|
||||
padding: 12rpx 0 24rpx;
|
||||
}
|
||||
|
||||
.records-finished {
|
||||
display: block;
|
||||
padding: 8rpx 0;
|
||||
color: #9ca3af;
|
||||
font-size: 24rpx;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@
|
|||
<view class="merchant-label">
|
||||
<view class="merchant-label-inner">
|
||||
<text class="muted">向</text>
|
||||
<text class="merchant-name">{{ merchant.name }}</text>
|
||||
<text class="merchant-name" :style="{ fontSize: merchantNameFontSize }">{{ merchant.name }}</text>
|
||||
<text class="muted">付款</text>
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -88,7 +88,6 @@
|
|||
<view
|
||||
v-else
|
||||
class="property-row"
|
||||
:class="{ 'property-row--disabled': alipayPropertyUnbound }"
|
||||
hover-class="property-row-hover"
|
||||
@tap.stop="handlePropertyRow"
|
||||
>
|
||||
|
|
@ -189,7 +188,7 @@ import { computed, ref } from 'vue';
|
|||
import { onLoad, onShow } from '@dcloudio/uni-app';
|
||||
import { bindMerchantByUrl, cancelTrade, changeMerchant, merchantListByUrl, supplierByUrl } from '@/api/cashier';
|
||||
import type { MerchantListItem } from '@/api/cashier';
|
||||
import { bindMobile, resolveLoginPlatform } from '@/api/auth';
|
||||
import { bindMobile, fetchOpenInfo, resolveLoginPlatform } from '@/api/auth';
|
||||
import { postForm } from '@/utils/request';
|
||||
import { DEFAULT_MERCHANT, APP_CONFIG, PROPERTY_APP } from '@/config';
|
||||
import {
|
||||
|
|
@ -203,7 +202,7 @@ import {
|
|||
resolveMerchantUrlFromScanResult,
|
||||
syncActiveMerchantContext
|
||||
} from '@/utils/merchant-context';
|
||||
import { encryptedStorage } from '@/utils/storage';
|
||||
import { authStorage, encryptedStorage } from '@/utils/storage';
|
||||
import { formatPaymentError, isPaymentCancelled, requestPlatformPayment } from '@/utils/wxpay';
|
||||
|
||||
interface MerchantInfo {
|
||||
|
|
@ -247,11 +246,22 @@ const accumulationAmount = computed(() => {
|
|||
});
|
||||
const showBenefitTip = computed(() => Number(merchant.value.accumulationRate) > 0);
|
||||
const benefitTitle = computed(() => `预计获得${scoreName.value || '物业公积金'}`);
|
||||
/** 商户名越长字号越小,尽量单行展示「向 xxx 付款」 */
|
||||
const merchantNameFontSize = computed(() => {
|
||||
const len = String(merchant.value.name || '').trim().length;
|
||||
if (len <= 6) return '34rpx';
|
||||
if (len <= 10) return '30rpx';
|
||||
if (len <= 14) return '26rpx';
|
||||
if (len <= 18) return '24rpx';
|
||||
return '22rpx';
|
||||
});
|
||||
const propertyRowText = computed(() => {
|
||||
if (propertyName.value) return propertyName.value;
|
||||
if (propertyList.value.length) return '选择物业';
|
||||
if (IS_ALIPAY) {
|
||||
if (alipayPropertyUnbound.value) return '暂未绑定,请去微信小程序绑定';
|
||||
if (alipayPropertyUnbound.value) {
|
||||
return `未绑定,微信搜「${PROPERTY_APP.WECHAT_NAME}」绑定房源`;
|
||||
}
|
||||
return '绑定手机号查看房源';
|
||||
}
|
||||
return '业主去绑定';
|
||||
|
|
@ -260,7 +270,7 @@ const isPropertyActionText = computed(() => !propertyName.value);
|
|||
const showAlipayPhoneAuthBtn = computed(
|
||||
() => IS_ALIPAY && !propertyName.value && !propertyList.value.length && !alipayPropertyUnbound.value
|
||||
);
|
||||
const showPropertyArrow = computed(() => !IS_ALIPAY || !alipayPropertyUnbound.value);
|
||||
const showPropertyArrow = computed(() => true);
|
||||
const propertySheetTitle = computed(() => (propertyName.value ? '切换物业' : '选择物业'));
|
||||
|
||||
interface PropertyBindingCache {
|
||||
|
|
@ -582,6 +592,32 @@ const resolvePropertyListFallback = async (merchantUrl: string) => {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 支付宝:无物业名/列表时,用 open.info 的 user_id 判断是否已绑过手机号。
|
||||
* 有 user_id → 显示「未绑定,微信搜「乐享云…」绑定房源」;否则 → 「绑定手机号查看房源」。
|
||||
*/
|
||||
const resolveAlipayEmptyPropertyState = async (options: { assumeBoundIfEmpty?: boolean } = {}) => {
|
||||
if (!IS_ALIPAY) return;
|
||||
|
||||
if (propertyName.value || propertyList.value.length) {
|
||||
alipayPropertyUnbound.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetchOpenInfo();
|
||||
const userId = response?.data?.user?.user_id ?? response?.data?.open?.user_id;
|
||||
if (userId != null && String(userId).trim() !== '') {
|
||||
alipayPropertyUnbound.value = true;
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('open.info empty property state', error);
|
||||
}
|
||||
|
||||
alipayPropertyUnbound.value = !!options.assumeBoundIfEmpty;
|
||||
};
|
||||
|
||||
const loadPropertyBinding = async (merchantUrl: string, options: { skipFallback?: boolean } = {}) => {
|
||||
const cacheKey = syncBindingCacheForUrl(merchantUrl);
|
||||
const cached = encryptedStorage.get<PropertyBindingCache>(cacheKey);
|
||||
|
|
@ -595,17 +631,20 @@ const loadPropertyBinding = async (merchantUrl: string, options: { skipFallback?
|
|||
if (!options.skipFallback) {
|
||||
await resolvePropertyListFallback(merchantUrl);
|
||||
}
|
||||
await resolveAlipayEmptyPropertyState();
|
||||
return;
|
||||
}
|
||||
|
||||
const latestBinding = buildBindingCache(response.data || {});
|
||||
applyBindingInfo(latestBinding);
|
||||
encryptedStorage.set(cacheKey, latestBinding, PROPERTY_CACHE_TTL_SECONDS);
|
||||
await resolveAlipayEmptyPropertyState();
|
||||
} catch (error) {
|
||||
console.log('bind merchant fallback cache', error);
|
||||
if (!options.skipFallback) {
|
||||
await resolvePropertyListFallback(merchantUrl);
|
||||
}
|
||||
await resolveAlipayEmptyPropertyState();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -654,7 +693,7 @@ const deleteKey = () => {
|
|||
const showAlipayUnboundTip = () => {
|
||||
uni.showModal({
|
||||
title: '暂未绑定房源',
|
||||
content: '您在微信小程序中暂无绑定房源,请前往微信小程序完成绑定后再使用。',
|
||||
content: `请打开微信,搜索并进入「${PROPERTY_APP.WECHAT_NAME}」小程序,完成房源绑定后消费即可获得物业基金。`,
|
||||
showCancel: false,
|
||||
confirmText: '知道了'
|
||||
});
|
||||
|
|
@ -668,16 +707,17 @@ const refreshPropertyAfterPhoneBind = async () => {
|
|||
propertyList.value = [];
|
||||
encryptedStorage.remove(getPropertyBindingCacheKey(activeMerchantUrl.value));
|
||||
await loadPropertyBinding(activeMerchantUrl.value);
|
||||
// 刚绑完手机号仍无房源时,兜底按已绑定处理(open.info 偶发未带上 user_id)
|
||||
if (!propertyName.value && !propertyList.value.length && !alipayPropertyUnbound.value) {
|
||||
await resolveAlipayEmptyPropertyState({ assumeBoundIfEmpty: true });
|
||||
}
|
||||
|
||||
if (propertyName.value || propertyList.value.length) {
|
||||
uni.showToast({
|
||||
title: '已匹配到物业信息',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
alipayPropertyUnbound.value = true;
|
||||
};
|
||||
|
||||
const tryParseJson = (value: any) => {
|
||||
|
|
@ -775,6 +815,13 @@ const handleAlipayPhoneAuthorize = async (event?: any) => {
|
|||
throw new Error(response?.msg || '手机号绑定失败');
|
||||
}
|
||||
|
||||
const nextToken = String(response?.data?.token || '').trim();
|
||||
if (!nextToken) {
|
||||
throw new Error('绑定成功但未返回 token');
|
||||
}
|
||||
authStorage.setToken(nextToken, Number(response?.data?.expires_in || 0));
|
||||
logPhoneAuth('已替换登录 token', { token: nextToken });
|
||||
|
||||
await refreshPropertyAfterPhoneBind();
|
||||
} catch (error: any) {
|
||||
logPhoneAuth('授权失败', error);
|
||||
|
|
@ -1232,6 +1279,7 @@ onShow(() => {
|
|||
.merchant-label-inner {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
flex-wrap: nowrap;
|
||||
max-width: 88%;
|
||||
padding: 14rpx 32rpx;
|
||||
border-radius: 999rpx;
|
||||
|
|
@ -1239,16 +1287,22 @@ onShow(() => {
|
|||
}
|
||||
|
||||
.muted {
|
||||
flex-shrink: 0;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.merchant-name {
|
||||
min-width: 0;
|
||||
margin: 0 8rpx;
|
||||
color: #FFFFFF;
|
||||
font-size: 34rpx;
|
||||
font-weight: 800;
|
||||
line-height: 1.3;
|
||||
text-shadow: 0 1rpx 6rpx rgba(0, 0, 0, 0.35);
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.amount-zone {
|
||||
|
|
@ -1388,6 +1442,8 @@ onShow(() => {
|
|||
.property-name-tip {
|
||||
color: #8B929E;
|
||||
font-weight: 500;
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 4rpx;
|
||||
}
|
||||
|
||||
.property-arrow {
|
||||
|
|
|
|||
|
|
@ -33,13 +33,14 @@
|
|||
</view>
|
||||
|
||||
<view v-if="records.length && (loading || finished)" class="records-footer">
|
||||
<wd-loadmore :state="loadMoreState" />
|
||||
<wd-loadmore v-if="loading" state="loading" />
|
||||
<text v-else class="records-finished">没有更多了</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue';
|
||||
import { ref } from 'vue';
|
||||
import { onLoad, onPullDownRefresh, onReachBottom } from '@dcloudio/uni-app';
|
||||
import { tradeList } from '@/api/cashier';
|
||||
import type { TradeListItem } from '@/api/cashier';
|
||||
|
|
@ -54,8 +55,6 @@ const finished = ref(false);
|
|||
|
||||
const emptyImage = `${APP_CONFIG.RESOURCE_URL}tip/message.png`;
|
||||
|
||||
const loadMoreState = computed(() => (loading.value ? 'loading' : 'finished'));
|
||||
|
||||
const formatMoney = (value?: string | number) => {
|
||||
const amount = Number(value || 0);
|
||||
if (!Number.isFinite(amount)) return '0.00';
|
||||
|
|
@ -262,4 +261,12 @@ onReachBottom(() => {
|
|||
.records-footer {
|
||||
padding: 12rpx 0 24rpx;
|
||||
}
|
||||
|
||||
.records-finished {
|
||||
display: block;
|
||||
padding: 8rpx 0;
|
||||
color: #9ca3af;
|
||||
font-size: 24rpx;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
Loading…
Reference in New Issue