This commit is contained in:
Austin 2026-07-15 15:54:56 +08:00
parent 9d8b85987b
commit d61d8e67a1
5 changed files with 117 additions and 22 deletions

View File

@ -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');

View File

@ -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/',

View File

@ -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>

View File

@ -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 {

View File

@ -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>