217 lines
6.5 KiB
TypeScript
217 lines
6.5 KiB
TypeScript
import { request } from '../../utils/request'
|
||
import { QuotaItem, UserInfo, GoodsItem, QuotaFlowItem, PagedResult, VirtualPayResult, OrderItem } from '../../types/models'
|
||
import { QUOTA_NAMES, GoodsType, GOODS_TYPE_NAMES } from '../../utils/config'
|
||
import { fenToYuan, formatDate, formatDateTime } from '../../utils/format'
|
||
|
||
const GOODS_ICONS: Record<number, string> = {
|
||
[GoodsType.MEMBER]: 'icon-crown',
|
||
[GoodsType.UPLOAD]: 'icon-camera',
|
||
[GoodsType.AI]: 'icon-sparkles',
|
||
[GoodsType.CLOSET_SHARE]: 'icon-hanger',
|
||
[GoodsType.OUTFIT_SHARE]: 'icon-shirt',
|
||
}
|
||
|
||
const GOODS_FILTERS = [
|
||
{ name: '全部', value: 0 },
|
||
{ name: '会员包', value: 1 },
|
||
{ name: '上传包', value: 2 },
|
||
{ name: 'AI 推荐包', value: 3 },
|
||
{ name: '衣柜分享包', value: 4 },
|
||
{ name: '搭配分享包', value: 5 },
|
||
]
|
||
|
||
type PayPlatform = 'android' | 'ios' | 'harmony' | 'windows'
|
||
|
||
// 归一化平台:后端仅接受 android/ios/harmony/windows,devtools/mac 等调试环境默认按 android 处理
|
||
function normalizePlatform(): PayPlatform {
|
||
let p = 'android'
|
||
try {
|
||
p = wx.getSystemInfoSync().platform
|
||
} catch (e) {
|
||
// 获取失败时回退 android,避免请求体缺失导致参数校验失败
|
||
}
|
||
if (p === 'ios' || p === 'harmony' || p === 'windows') return p
|
||
return 'android'
|
||
}
|
||
|
||
Page({
|
||
data: {
|
||
view: 'mall' as 'mall' | 'flow',
|
||
userInfo: {} as UserInfo,
|
||
isVip: false,
|
||
vipExpireText: '',
|
||
quotas: [] as QuotaItem[],
|
||
goodsFilters: GOODS_FILTERS,
|
||
activeFilter: 0,
|
||
goods: [] as GoodsItem[],
|
||
flowList: [] as QuotaFlowItem[],
|
||
goodsIcons: GOODS_ICONS,
|
||
orderVisible: false,
|
||
orderGoods: null as GoodsItem | null,
|
||
loading: true,
|
||
},
|
||
|
||
onLoad(query: Record<string, string>) {
|
||
if (query.tab === 'flow') this.setData({ view: 'flow' })
|
||
},
|
||
|
||
onShow() {
|
||
this.fetchUser()
|
||
this.fetchQuota()
|
||
this.fetchGoods()
|
||
if (this.data.view === 'flow') this.fetchFlow()
|
||
},
|
||
|
||
fetchUser() {
|
||
request<UserInfo>({ url: '/user/info', showError: false })
|
||
.then((user) => {
|
||
const isVip = user.vip_type !== 1
|
||
this.setData({
|
||
userInfo: user,
|
||
isVip,
|
||
vipExpireText: isVip && user.vip_expire ? `有效期至 ${formatDate(user.vip_expire)}` : '',
|
||
})
|
||
})
|
||
.catch(() => {})
|
||
},
|
||
|
||
fetchQuota() {
|
||
request<{ quotas: QuotaItem[] }>({ url: '/quota/info', showError: false, silentAuth: true })
|
||
.then((res) => {
|
||
const quotas = res.quotas.map((q) => ({
|
||
...q,
|
||
_name: QUOTA_NAMES[q.quota_type] || '额度',
|
||
_percent: q.total > 0 ? Math.round((q.used / q.total) * 100) : 0,
|
||
}))
|
||
this.setData({ quotas })
|
||
})
|
||
.catch(() => {})
|
||
},
|
||
|
||
fetchGoods() {
|
||
const params: Record<string, any> = { page: 1, page_size: 50 }
|
||
const filter = this.data.activeFilter
|
||
if (filter > 0) params.goods_type = filter
|
||
request<PagedResult<GoodsItem>>({ url: '/goods', data: params, auth: false })
|
||
.then((res) => {
|
||
const goods = res.list.map((g) => ({
|
||
...g,
|
||
_icon: GOODS_ICONS[g.goods_type] || 'icon-crown',
|
||
_typeName: GOODS_TYPE_NAMES[g.goods_type] || '',
|
||
_priceText: fenToYuan(g.discount_price),
|
||
_originText: fenToYuan(g.price),
|
||
}))
|
||
this.setData({ goods, loading: false })
|
||
})
|
||
.catch(() => this.setData({ loading: false }))
|
||
},
|
||
|
||
fetchFlow() {
|
||
request<PagedResult<QuotaFlowItem>>({ url: '/quota/flow', data: { page: 1, page_size: 50 } })
|
||
.then((res) => {
|
||
const list = res.list.map((it) => ({
|
||
...it,
|
||
_time: formatDateTime(it.created_at),
|
||
_sign: it.change_num > 0 ? `+${it.change_num}` : `${it.change_num}`,
|
||
_name: QUOTA_NAMES[it.quota_type] || '额度',
|
||
}))
|
||
this.setData({ flowList: list })
|
||
})
|
||
.catch(() => {})
|
||
},
|
||
|
||
onGoodsFilter(e: WechatMiniprogram.TouchEvent) {
|
||
const index = Number(e.currentTarget.dataset.index)
|
||
if (index === this.data.activeFilter) return
|
||
this.setData({ activeFilter: index, loading: true })
|
||
this.fetchGoods()
|
||
},
|
||
|
||
onTapMember() {
|
||
this.setData({ activeFilter: 1, loading: true })
|
||
this.fetchGoods()
|
||
},
|
||
|
||
onSwitchMall() {
|
||
this.setData({ view: 'mall' })
|
||
},
|
||
onSwitchFlow() {
|
||
this.setData({ view: 'flow' })
|
||
this.fetchFlow()
|
||
},
|
||
|
||
onBuy(e: WechatMiniprogram.TouchEvent) {
|
||
const goods = e.currentTarget.dataset.goods as GoodsItem
|
||
this.setData({ orderGoods: goods, orderVisible: true })
|
||
},
|
||
|
||
onCloseOrder() {
|
||
this.setData({ orderVisible: false })
|
||
},
|
||
noop() {},
|
||
|
||
confirmPay() {
|
||
if (!this.data.orderGoods) return
|
||
request<VirtualPayResult>({
|
||
url: '/order/virtual',
|
||
method: 'POST',
|
||
data: {
|
||
goods_code: this.data.orderGoods.goods_code,
|
||
platform: normalizePlatform(),
|
||
},
|
||
})
|
||
.then((res) => {
|
||
this.setData({ orderVisible: false })
|
||
this.doPayment(res)
|
||
})
|
||
.catch(() => {})
|
||
},
|
||
|
||
doPayment(res: VirtualPayResult) {
|
||
wx.requestVirtualPayment({
|
||
signData: res.sign_data,
|
||
paySig: res.pay_sig,
|
||
signature: res.signature,
|
||
mode: 'short_series_goods',
|
||
success: () => {
|
||
// 拉起成功仅代表进入支付页,最终到账以后端发货推送为准,轮询订单确认
|
||
this.pollOrder(res.order_no)
|
||
},
|
||
fail: () => {},
|
||
})
|
||
},
|
||
|
||
pollOrder(orderNo: string) {
|
||
let count = 0
|
||
const timer = setInterval(() => {
|
||
count++
|
||
request<PagedResult<OrderItem>>({ url: '/order', data: { page: 1, page_size: 50 }, showError: false })
|
||
.then((res) => {
|
||
const order = res.list.find((it) => it.order_no === orderNo)
|
||
if (order && order.order_status === 2) {
|
||
clearInterval(timer)
|
||
wx.showToast({ title: '支付成功', icon: 'success' })
|
||
this.fetchQuota()
|
||
this.fetchUser()
|
||
} else if (order && (order.order_status === 3 || order.order_status === 4 || order.order_status === 5)) {
|
||
clearInterval(timer)
|
||
const msg = order.order_status === 5 ? '订单已退款' : '订单未支付成功'
|
||
wx.showToast({ title: msg, icon: 'none' })
|
||
} else if (count >= 10) {
|
||
this.finishPayTimeout(timer)
|
||
}
|
||
})
|
||
.catch(() => {
|
||
if (count >= 10) this.finishPayTimeout(timer)
|
||
})
|
||
}, 1500)
|
||
},
|
||
|
||
finishPayTimeout(timer: ReturnType<typeof setInterval>) {
|
||
clearInterval(timer)
|
||
wx.showToast({ title: '到账可能有延迟,请稍后查看', icon: 'none' })
|
||
this.fetchQuota()
|
||
this.fetchUser()
|
||
},
|
||
})
|