This commit is contained in:
Felix
2025-12-23 17:33:05 +08:00
parent 023b30ec87
commit 4f1aff4a62
12 changed files with 202 additions and 75 deletions

View File

@@ -587,8 +587,22 @@ Page<IPageData, IPageInstance>({
// 长按开始录音
handleRecordStart() {
if (this.data.isRecording) return
this.startRecording()
try { this.onMicHighlight() } catch (e) {}
try {
wx.getSetting({
success: (res) => {
const granted = !!(res.authSetting && res.authSetting['scope.record'])
if (granted) {
this.setData({ recordPermissionGranted: true })
this.startRecording()
try { this.onMicHighlight() } catch (e) {}
return
}
try { this.ensureRecordPermission() } catch (_) {}
}
})
} catch (e) {
try { this.ensureRecordPermission() } catch (_) {}
}
},
// 松开结束录音

View File

@@ -41,6 +41,8 @@ Page({
...it,
displayTime: this.formatTime(it.created_time),
displayAmount: this.formatAmount(it.amount_cents),
refund_display: this.formatRefundStatusText(it.refund_status),
refund_class: this.formatRefundStatusClass(it.refund_status)
}))
logger.info(items)
if (page === 1) {
@@ -74,6 +76,25 @@ Page({
return (cents / 100).toFixed(2)
},
formatRefundStatusText(s: string | null | undefined) {
const v = String(s || '').trim().toUpperCase()
if (!v) return '未申请退款'
if (v === 'REFUND') return '已退款'
if (v === 'FAIL') return '退款失败'
if (v === 'CLOSED') return '退款已关闭'
if (v === 'ABNORMAL') return '退款异常'
return s as string
},
formatRefundStatusClass(s: string | null | undefined) {
const v = String(s || '').trim().toUpperCase()
if (v === 'REFUND') return 'status-success'
if (v === 'FAIL') return 'status-failed'
if (v === 'CLOSED') return 'status-closed'
if (v === 'ABNORMAL') return 'status-abnormal'
return 'status-default'
},
async onRefundTap(e: any) {
const id = e.currentTarget.dataset.id
try {
@@ -81,6 +102,8 @@ Page({
const updated = this.data.orders.map((it: any) => it.id === id ? {
...it,
refund_status: detail.refund_status || it.refund_status,
refund_display: this.formatRefundStatusText(detail.refund_status || it.refund_status),
refund_class: this.formatRefundStatusClass(detail.refund_status || it.refund_status),
amount_cents: detail.amount_cents ?? it.amount_cents,
displayAmount: this.formatAmount(detail.amount_cents ?? it.amount_cents),
refundable_amount_cents: detail.refundable_amount_cents ?? it.refundable_amount_cents,
@@ -134,7 +157,7 @@ Page({
try {
const s = await apiManager.getRefundStatus(outRefundNo)
const status = s && (s.status || '')
const updated = this.data.orders.map((it: any) => it.id === this.data.refundOrderId ? { ...it, refund_status: status, can_refund: false } : it)
const updated = this.data.orders.map((it: any) => it.id === this.data.refundOrderId ? { ...it, refund_status: status, refund_display: this.formatRefundStatusText(status), refund_class: this.formatRefundStatusClass(status), can_refund: false } : it)
this.setData({ orders: updated })
wx.showToast({ title: status ? `退款状态:${status}` : '查询成功', icon: 'none' })
} catch (_) {

View File

@@ -12,7 +12,7 @@
</view>
<view slot="right-icon">
<view wx:if="{{!item.refund_status}}" class="refund-btn" bindtap="onRefundTap" data-id="{{item.id}}">退款</view>
<view wx:else class="refund-status">{{item.refund_status}}</view>
<view wx:else class="refund-status {{item.refund_class}}">{{item.refund_display}}</view>
</view>
</t-cell>
</block>

View File

@@ -5,6 +5,12 @@
.order-desc { color: #666; font-size: 26rpx; }
.refund-btn { color: #0052d9; font-size: 26rpx; padding: 8rpx 16rpx; border: 1rpx solid #0052d9; border-radius: 8rpx; }
.refund-status { color: #999; font-size: 26rpx; padding: 8rpx 0; }
.refund-status.status-success { color: #27ae60; }
.refund-status.status-processing { color: #f39c12; }
.refund-status.status-failed { color: #e74c3c; }
.refund-status.status-closed { color: #999999; }
.refund-status.status-abnormal { color: #e67e22; }
.refund-status.status-default { color: #666666; }
.refund-content { padding: 12rpx 0; }
.refund-textarea { width: 100%; min-height: 160rpx; border: 1rpx solid #ddd; border-radius: 8rpx; padding: 12rpx; box-sizing: border-box; margin-top: 12rpx; }
.reason-tip { color: #999; font-size: 24rpx; margin-top: 8rpx; }

View File

@@ -19,6 +19,10 @@ Page({
// 积分数据
points: {
balance: 0,
available_balance: 0,
frozen_balance: 0,
total_purchased: 0,
total_refunded: 0,
expired_time: ''
},
@@ -290,7 +294,7 @@ Page({
const pointsData = await apiManager.getPointsData()
// 如果返回的数据为null则设置默认值
const finalPointsData = pointsData || { balance: 0, expired_time: '' }
const finalPointsData = pointsData || { balance: 0, available_balance: 0, frozen_balance: 0, total_purchased: 0, total_refunded: 0, expired_time: '' }
const endTime = Date.now()
logger.info('积分数据加载完成,耗时:', endTime - startTime, 'ms', finalPointsData)
@@ -301,6 +305,10 @@ Page({
this.setData({
points: {
balance: 0,
available_balance: 0,
frozen_balance: 0,
total_purchased: 0,
total_refunded: 0,
expired_time: ''
}
})

View File

@@ -69,11 +69,11 @@
maxcharacter="{{6}}"
/>
</t-dialog>
<navigator url="/pages/coupon/coupon?points={{points.balance}}" class="cell-navigator">
<t-cell title="积分" hover note="{{points.balance}}">
<t-icon slot="left-icon" name="star" size="44rpx"></t-icon>
</t-cell>
</navigator>
<navigator url="/pages/coupon/coupon?points={{points.available_balance}}" class="cell-navigator">
<t-cell title="积分" hover note="{{points.available_balance}}">
<t-icon slot="left-icon" name="star" size="44rpx"></t-icon>
</t-cell>
</navigator>
<navigator url="/pages/order/order" class="cell-navigator">
<t-cell title="订单记录" hover arrow>
<t-icon slot="left-icon" name="shop" size="44rpx"></t-icon>

View File

@@ -119,6 +119,7 @@ interface IPageInstance {
updateCircleProgress: () => void
playAssessmentVoice: () => void
noop: () => void
ensureRecordPermission: () => void
startRecording: () => void
stopRecording: () => void
onMicHighlight: () => void
@@ -196,6 +197,7 @@ Page<IData, IPageInstance>({
wx.showToast({ title: '缺少 image_id', icon: 'none' })
return
}
this.ensureRecordPermission()
this.setData({ loadingMaskVisible: true, statusText: '加载中...', currentIndex: 0, transDisplayMode: 'en_zh', contentVisible: false, isPlaying: false, isRecording: false, hasScoreInfo: false, isScoreModalOpen: false, scoreModalVisible: false })
this.startLoadingDots()
if (!this.audioCtx) {
@@ -739,8 +741,22 @@ Page<IData, IPageInstance>({
handleRecordStart() {
if (this.data.isRecording) return
this.startRecording()
try { this.onMicHighlight() } catch (e) {}
try {
wx.getSetting({
success: (res) => {
const granted = !!(res.authSetting && res.authSetting['scope.record'])
if (granted) {
this.setData({ recordPermissionGranted: true })
this.startRecording()
try { this.onMicHighlight() } catch (e) {}
return
}
try { this.ensureRecordPermission() } catch (_) {}
}
})
} catch (e) {
try { this.ensureRecordPermission() } catch (_) {}
}
},
handleRecordEnd() {
@@ -1040,6 +1056,45 @@ Page<IData, IPageInstance>({
}, 0)
}
},
ensureRecordPermission() {
try {
wx.getSetting({
success: (res) => {
const granted = !!(res.authSetting && res.authSetting['scope.record'])
if (granted) {
this.setData({ recordPermissionGranted: true })
return
}
wx.authorize({
scope: 'scope.record',
success: () => {
this.setData({ recordPermissionGranted: true })
},
fail: () => {
wx.showModal({
title: '需要麦克风权限',
content: '录音功能需要麦克风权限,请在设置中开启',
confirmText: '去设置',
cancelText: '取消',
success: (r) => {
if (r.confirm) {
wx.openSetting({
success: (s) => {
const ok = !!(s.authSetting && s.authSetting['scope.record'])
this.setData({ recordPermissionGranted: ok })
}
})
}
}
})
}
})
}
})
} catch (e) {}
},
computeHighlightLayout() {
const list = this.data.scene?.list || []
const cur = list[this.data.currentIndex]

View File

@@ -833,37 +833,40 @@ Page({
const deltaRpx = (expandBottomRpx - expandTopRpx - (photoBottomRpx - photoTopRpx)) / 2
const pxPerRpx = (win.windowWidth || vw) / 750
dy += deltaRpx * pxPerRpx
const startAtPhoto = `transform: translate(${dx}px, ${dy}px) scale(${scale});`
this.setData({
photoExpandTransform: startAtPhoto,
photoExpandTransition: 'transition: transform 0ms',
showExpandLayer: true,
photoExpandCurrentWidth: Math.round(rect.width),
expandBorderStyle: 'opacity: 0;'
})
setTimeout(() => {
this.setData({ photoExpandTransition: 'transition: transform 900ms ease-in-out' })
this.setData({ photoExpandTransform: 'transform: translate(0px, 0px) scale(1);', photoExpandCurrentWidth: targetWidth, expandBorderStyle: 'opacity: 1; transition: opacity 600ms ease-out;' })
setTimeout(() => { this.setData({ scannerVisible: true }) }, 100)
}, 50)
const startAtPhoto = `transform: translate(${dx}px, ${dy}px) scale(${scale});`
const targetHeight = Math.round(rect.height / scale)
this.setData({
photoExpandTransform: startAtPhoto,
photoExpandTransition: 'transition: transform 0ms',
photoExpandCurrentWidth: targetWidth,
photoExpandCurrentHeight: targetHeight,
showExpandLayer: true,
expandBorderStyle: 'opacity: 0;'
})
setTimeout(() => {
this.setData({ photoExpandTransition: 'transition: transform 900ms ease-in-out' })
this.setData({ photoExpandTransform: 'transform: translate(0px, 0px) scale(1);', expandBorderStyle: 'opacity: 1; transition: opacity 600ms ease-out;' })
setTimeout(() => { this.setData({ scannerVisible: true }) }, 100)
}, 50)
})
}, waitMs)
} catch (e) {}
},
resetPageState() {
this.setData({
takePhoto: false,
photoPath: '',
photoImageLoaded: false,
photoExpandLoaded: false,
showExpandLayer: false,
photoExpandTransform: '',
photoExpandTransition: '',
photoExpandCurrentWidth: 0,
expandBorderStyle: ''
})
},
resetPageState() {
this.setData({
takePhoto: false,
photoPath: '',
photoImageLoaded: false,
photoExpandLoaded: false,
showExpandLayer: false,
photoExpandTransform: '',
photoExpandTransition: '',
photoExpandCurrentWidth: 0,
photoExpandCurrentHeight: 0,
expandBorderStyle: ''
})
},
// 跳转到结果页面
async navigateToResult() {

View File

@@ -41,8 +41,7 @@
</view>
</view>
<view wx:if="{{takePhoto && showExpandLayer }}" class="photo-expand-layer" style="{{photoExpandTransform}} {{photoExpandTransition}}">
<view class="photo-expand-inner" style="{{photoExpandCurrentWidth ? ('width: ' + photoExpandCurrentWidth + 'px;') : ''}}">
<!-- <view class="expand-border-bg" style="{{expandBorderStyle}}"></view> -->
<view class="photo-expand-inner" style="{{photoExpandCurrentWidth ? ('width: ' + photoExpandCurrentWidth + 'px;') : ''}} {{photoExpandCurrentHeight ? ('height: ' + photoExpandCurrentHeight + 'px;') : ''}}">
<image class="photo-expand-image" src="{{takePhoto ? photoExpandSrc : photoSvgData}}" mode="widthFix" bindload="onPhotoExpandLoaded" binderror="onPhotoExpandError"></image>
<view class="scanner {{scannerVisible ? 'scanner-visible' : 'scanner-hidden'}}">
<view class="star star1"></view>

View File

@@ -1196,37 +1196,44 @@
height: auto;
}
.photo-expand-layer {
position: fixed;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
pointer-events: none;
z-index: 99;
transition: transform 900ms ease-in-out;
}
.photo-expand-inner {
display: inline-block;
/* 初始与小卡片重叠,无额外外边距 */
background-color: #ffffff;
border: 24rpx solid #ffffff;
border-bottom: 48rpx solid #fff;
box-shadow: 0 12rpx 32rpx rgba(0,0,0,0.35);
border-radius: 12rpx;
position: relative;
box-sizing: border-box;
transition: width 900ms ease-in-out;
}
.photo-expand-image {
display: block;
width: 100%;
height: auto;
max-width: 100%;
position: relative;
border-radius: 12rpx;
}
.photo-expand-layer {
position: fixed;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
pointer-events: none;
z-index: 99;
transition: transform 900ms ease-in-out;
will-change: transform;
transform-origin: center center;
backface-visibility: hidden;
}
.photo-expand-inner {
display: inline-block;
/* 初始与小卡片重叠,无额外外边距 */
background-color: #ffffff;
border: 24rpx solid #ffffff;
border-bottom: 48rpx solid #fff;
box-shadow: 0 12rpx 32rpx rgba(0,0,0,0.35);
border-radius: 12rpx;
position: relative;
box-sizing: border-box;
transition: width 900ms ease-in-out;
will-change: width, transform;
transform-origin: center center;
backface-visibility: hidden;
}
.photo-expand-image {
display: block;
width: 100%;
height: auto;
max-width: 100%;
position: relative;
border-radius: 12rpx;
backface-visibility: hidden;
}
.scanner {
position: absolute;

View File

@@ -68,6 +68,10 @@ export interface ExtendedWordDetail {
// 积分数据接口
export interface IPointsData {
balance: number
available_balance: number
frozen_balance: number
total_purchased: number
total_refunded: number
expired_time: string
}

View File

@@ -246,7 +246,7 @@ class ApiManager {
const maxRetries = 1 // 最多重试1次首次请求 + 1次重试
if (USE_CLOUD){
return this.wx_request<T>(url, method, data, showLoading)
return this.wx_request<T>(url, method, data, showLoading) as unknown as IApiResponse<T>
}
else {
return new Promise(async (resolve, reject) => {
@@ -1926,10 +1926,18 @@ class ApiManager {
// 如果返回的数据为null则返回默认值
if (!response.data) {
return { balance: 0, expired_time: '' };
return { balance: 0, available_balance: 0, frozen_balance: 0, total_purchased: 0, total_refunded: 0, expired_time: '' };
}
return response.data;
const d: any = response.data as any
const normalized = {
balance: Number(d.balance || 0),
available_balance: Number(d.available_balance || d.balance || 0),
frozen_balance: Number(d.frozen_balance || 0),
total_purchased: Number(d.total_purchased || 0),
total_refunded: Number(d.total_refunded || 0),
expired_time: String(d.expired_time || '')
}
return normalized as import("../types/app").IPointsData;
} catch (error) {
console.error('获取积分数据失败:', error);
throw error;