Files
ruo-shan-cloud-pharmacy/miniprogram/utils/util.ts
T
陈誉午 994b267f5c feat: 电子药箱小程序 - 4Tab药品管理
- 电子药箱: 药品库存管理 + 手动录入 + 今日用药打卡
- 药品百科: 搜索 + 分类筛选 + 20种药品静态数据
- 惠教中心: 4条药品使用指南课程
- 我的: 家庭成员信息管理
- 自定义TabBar + navigation-bar组件
- SVG药品分类插图
2026-07-29 11:20:52 +08:00

139 lines
4.1 KiB
TypeScript

import { request } from "./request";
export const formatTime = (date: Date) => {
const year = date.getFullYear()
const month = date.getMonth() + 1
const day = date.getDate()
const hour = date.getHours()
const minute = date.getMinutes()
const second = date.getSeconds()
return (
[year, month, day].map(formatNumber).join('/') +
' ' +
[hour, minute, second].map(formatNumber).join(':')
)
}
const formatNumber = (n: number) => {
const s = n.toString()
return s[1] ? s : '0' + s
}
export function useSecondsToHMS(seconds: number) {
const hour = Math.floor(seconds / 3600);
const minute = Math.floor((seconds - hour * 3600) / 60);
const second = Math.floor(seconds - hour * 3600 - minute * 60);
let str = '';
if (hour > 0) str += `${hour.toString().padStart(2, '0')}:`;
str += `${minute.toString().padStart(2, '0')}:`;
str += `${second.toString().padStart(2, '0')}`;
return str;
}
/**将后端传过来的绝对值px转为相对值rpx,
*1rpx为屏幕宽度的750分之1,后台模拟界面写死为375px,
*所以1rpx为375/750=0.5px 1px为2rpx
*/
export function toRpx(px: number): number {
return 2 * px
}
/**
* 领取红包并拉起微信转账
* @param data - 动态请求数据对象
* @param url - 请求地址
* @param appId - 当前小程序的 appId (也可选择从接口返回或 getAccountInfoSync 获取)
* @returns Promise<void>
*/
export async function receiveRedPacket(data: any, url: string, appId: string): Promise<void> {
wx.showLoading({ title: "领取中", mask: true });
// 建议移除 3s 延迟。如果为了防抖,建议在外部触发点击事件时处理
await new Promise(resolve => setTimeout(resolve, 3000));
try {
const res = await request<any>({
options: {
url,
method: "POST",
data
},
isLoading: false,
});
wx.hideLoading();
if (res.code !== 200) {
const title = res.msg || "领取失败";
const duration = res.code === 503 ? 3000 : 1500;
wx.showToast({ title, icon: "none", duration });
throw new Error(res.msg || "领取失败");
}
const { packageInfo, mchId } = res.data || {};
if (!packageInfo || !mchId) {
wx.showToast({ title: "领取失败", icon: "none" });
throw new Error("领取失败");
}
if (wx.canIUse?.("requestMerchantTransfer")) {
// 把回调式 API 包装成 Promise,让调用方能正确捕获成功/失败
await new Promise<void>((resolve, reject) => {
// @ts-ignore
wx.requestMerchantTransfer({
mchId,
appId,
package: packageInfo,
success: () => {
wx.showToast({ title: "领取成功", icon: "success" });
resolve();
},
fail: (err: any) => {
console.error("拉起转账失败:", err);
wx.showToast({ title: "领取失败", icon: "none" });
reject(new Error(err?.errMsg || "领取失败"));
},
});
});
} else {
wx.showModal({
content: "你的微信版本过低,请更新至最新版本。",
showCancel: false,
});
throw new Error("微信版本过低");
}
} catch (err: any) {
wx.hideLoading();
const msg = err?.data?.msg || err?.errMsg || err?.message || "领取失败";
wx.showToast({ title: msg, icon: "none" });
throw err;
}
}
/**
* 异步函数防并发锁
* 直接用 lockPromise 包裹原有的点击逻辑
handleReceiveClick: lockPromise(async function() {
await receiveRedPacket(data, url, appId);
})
*/
/**
* 异步函数防并发锁
* @param fn - 需要执行的异步函数
*/
export function lockPromise<T extends any[], R>(
fn: (this: any, ...args: T) => Promise<R> | R
): (this: any, ...args: T) => Promise<R | void> {
let isLocked = false;
return async function (this: any, ...args: T): Promise<R | void> {
// 如果正在执行中,直接返回,返回值为 void
if (isLocked) return;
isLocked = true;
try {
// 加上 return,确保原函数的返回值能够被向外传递
return await fn.apply(this, args);
} finally {
isLocked = false;
}
};
}