feat(pages): 添加多个页面配置和功能模块

- 新增 .editorconfig、.eslintrc、.gitignore 配置文件
- 添加管理员文章管理页面配置和功能实现
- 添加经销商申请注册页面配置和功能实现
- 添加经销商银行卡管理页面配置和功能实现
- 添加经销商客户管理页面配置和功能实现
- 添加用户地址管理页面配置和功能实现
- 添加用户聊天消息页面配置和功能实现
- 添加用户礼品管理页面配置和功能实现
This commit is contained in:
2026-01-08 13:36:06 +08:00
commit 43106acc27
548 changed files with 75931 additions and 0 deletions

171
src/utils/common.ts Normal file
View File

@@ -0,0 +1,171 @@
import Taro from '@tarojs/taro'
export default function navTo(url: string, isLogin = false) {
if (isLogin) {
if (!Taro.getStorageSync('access_token') || !Taro.getStorageSync('UserId')) {
Taro.showToast({
title: '请先登录',
icon: 'none',
duration: 500
});
return false;
}
}
Taro.navigateTo({
url: url
})
}
// 转base64
export function fileToBase64(filePath: string) {
return new Promise((resolve) => {
let fileManager = Taro.getFileSystemManager();
fileManager.readFile({
filePath,
encoding: 'base64',
success: (e: any) => {
resolve(`data:image/jpg;base64,${e.data}`);
}
});
});
};
/**
* 转义微信富文本图片样式
* @param htmlText
*/
export function wxParse(htmlText: string) {
// Replace <img> tags with max-width, height and margin styles to remove spacing
htmlText = htmlText.replace(/\<img/gi, '<img style="max-width:100%;height:auto;margin:0;padding:0;display:block;"');
// Replace style attributes that do not contain text-align, add margin:0 to remove spacing
htmlText = htmlText.replace(/style\s*?=\s*?(['"])(?!.*?text-align)[\s\S]*?\1/ig, 'style="max-width:100%;height:auto;margin:0;padding:0;display:block;"');
return htmlText;
}
export function copyText(text: string) {
Taro.setClipboardData({
data: text,
success: function () {
Taro.showToast({
title: '复制成功',
icon: 'success',
duration: 2000
});
},
fail: function () {
Taro.showToast({
title: '复制失败',
icon: 'none',
duration: 2000
});
}
});
}
/**
* 分享商品链接
* @param goodsId 商品ID
*/
export function shareGoodsLink(goodsId: string | number) {
// 构建分享链接,这里需要根据你的实际域名调整
const baseUrl = 'https://your-domain.com'; // 请替换为你的实际域名
const shareUrl = `${baseUrl}/shop/goodsDetail/index?id=${goodsId}`;
copyText(shareUrl);
}
/**
* 显示分享引导提示
*/
export function showShareGuide() {
Taro.showModal({
title: '分享提示',
content: '请点击右上角的"..."按钮,然后选择"转发"来分享给好友,或选择"分享到朋友圈"',
showCancel: false,
confirmText: '知道了'
});
}
/**
* 截取字符串,确保不超过指定的汉字长度
* @param text 原始文本
* @param maxLength 最大汉字长度默认30
* @returns 截取后的文本
*/
export function truncateText(text: string, maxLength: number = 30): string {
if (!text) return '';
// 如果长度不超过限制,直接返回
if (text.length <= maxLength) {
return text;
}
// 超过长度则截取
return text.substring(0, maxLength);
}
/**
* 格式化货币
* @param amount
* @param currency
*/
export function formatCurrency(amount: number, currency: string = 'CNY'): string {
return new Intl.NumberFormat('zh-CN', {
style: 'currency',
currency: currency,
minimumFractionDigits: 2,
maximumFractionDigits: 2
}).format(amount);
}
/**
* 生成订单标题
* @param goodsNames 商品名称数组
* @param maxLength 最大长度默认30
* @returns 订单标题
*/
export function generateOrderTitle(goodsNames: string[], maxLength: number = 30): string {
if (!goodsNames || goodsNames.length === 0) {
return '商品订单';
}
let title = '';
if (goodsNames.length === 1) {
title = goodsNames[0];
} else {
title = `${goodsNames[0]}${goodsNames.length}件商品`;
}
return truncateText(title, maxLength);
}
/**
* 下划线转驼峰命名
*/
export function toCamelCase(str: string): string {
return str.replace(/_([a-z])/g, function (_, letter) {
return letter.toUpperCase();
});
}
/**
* 下划线转大驼峰命名
*/
export function toCamelCaseUpper(str: string): string {
return toCamelCase(str).replace(/^[a-z]/, function (letter) {
return letter.toUpperCase();
});
}
/**
* 转为短下划线
*/
export function toShortUnderline(str: string): string {
return str.replace(/[A-Z]/g, function (letter) {
return '_' + letter.toLowerCase();
}).replace(/^_/, '');
}

200
src/utils/couponUtils.ts Normal file
View File

@@ -0,0 +1,200 @@
import { ShopUserCoupon } from '@/api/shop/shopUserCoupon/model'
import { CouponCardProps } from '@/components/CouponCard'
/**
* 将后端优惠券数据转换为前端组件所需格式
*/
export const transformCouponData = (coupon: ShopUserCoupon): CouponCardProps => {
// 解析金额
let amount = 0
if (coupon.type === 10) {
// 满减券使用reducePrice
amount = parseFloat(coupon.reducePrice || '0')
} else if (coupon.type === 20) {
// 折扣券使用discount
amount = coupon.discount || 0
} else if (coupon.type === 30) {
// 免费券金额为0
amount = 0
}
// 解析最低消费金额
const minAmount = parseFloat(coupon.minPrice || '0')
// 确定主题颜色
const getTheme = (type?: number): CouponCardProps['theme'] => {
switch (type) {
case 10: return 'red' // 满减券-红色
case 20: return 'orange' // 折扣券-橙色
case 30: return 'green' // 免费券-绿色
default: return 'blue'
}
}
return {
id: coupon.id,
amount,
minAmount: minAmount > 0 ? minAmount : undefined,
type: coupon.type as 10 | 20 | 30,
status: coupon.status as 0 | 1 | 2,
statusText: coupon.statusText,
title: coupon.name || coupon.description || '优惠券',
description: coupon.description,
startTime: coupon.startTime,
endTime: coupon.endTime,
isExpiringSoon: coupon.isExpiringSoon,
daysRemaining: coupon.daysRemaining,
hoursRemaining: coupon.hoursRemaining,
theme: getTheme(coupon.type)
}
}
/**
* 计算优惠券折扣金额
*/
export const calculateCouponDiscount = (
coupon: CouponCardProps,
totalAmount: number
): number => {
// 检查是否满足使用条件
if (coupon.minAmount && totalAmount < coupon.minAmount) {
return 0
}
// 检查优惠券状态
if (coupon.status !== 0) {
return 0
}
switch (coupon.type) {
case 10: // 满减券
return coupon.amount
case 20: // 折扣券
return totalAmount * (1 - coupon.amount / 10)
case 30: // 免费券
return totalAmount
default:
return 0
}
}
/**
* 检查优惠券是否可用
*/
export const isCouponUsable = (
coupon: CouponCardProps,
totalAmount: number
): boolean => {
// 状态检查
if (coupon.status !== 0) {
return false
}
// 金额条件检查
if (coupon.minAmount && totalAmount < coupon.minAmount) {
return false
}
return true
}
/**
* 获取优惠券不可用原因
*/
export const getCouponUnusableReason = (
coupon: CouponCardProps,
totalAmount: number
): string => {
if (coupon.status === 1) {
return '优惠券已使用'
}
if (coupon.status === 2) {
return '优惠券已过期'
}
if (coupon.minAmount && totalAmount < coupon.minAmount) {
return `需满${coupon.minAmount}元才能使用`
}
return ''
}
/**
* 格式化优惠券标题
*/
export const formatCouponTitle = (coupon: CouponCardProps): string => {
if (coupon.title) {
return coupon.title
}
switch (coupon.type) {
case 10: // 满减券
if (coupon.minAmount && coupon.minAmount > 0) {
return `${coupon.minAmount}${coupon.amount}`
}
return `立减${coupon.amount}`
case 20: // 折扣券
if (coupon.minAmount && coupon.minAmount > 0) {
return `${coupon.minAmount}${coupon.amount}`
}
return `${coupon.amount}折优惠`
case 30: // 免费券
return '免费券'
default:
return '优惠券'
}
}
/**
* 排序优惠券列表
* 按照优惠金额从大到小排序,同等优惠金额按过期时间排序
*/
export const sortCoupons = (
coupons: CouponCardProps[],
totalAmount: number
): CouponCardProps[] => {
return [...coupons].sort((a, b) => {
// 先按可用性排序
const aUsable = isCouponUsable(a, totalAmount)
const bUsable = isCouponUsable(b, totalAmount)
if (aUsable && !bUsable) return -1
if (!aUsable && bUsable) return 1
// 都可用或都不可用时,按优惠金额排序
const aDiscount = calculateCouponDiscount(a, totalAmount)
const bDiscount = calculateCouponDiscount(b, totalAmount)
if (aDiscount !== bDiscount) {
return bDiscount - aDiscount // 优惠金额大的在前
}
// 优惠金额相同时,按过期时间排序(即将过期的在前)
if (a.endTime && b.endTime) {
return new Date(a.endTime).getTime() - new Date(b.endTime).getTime()
}
return 0
})
}
/**
* 过滤可用优惠券
*/
export const filterUsableCoupons = (
coupons: CouponCardProps[],
totalAmount: number
): CouponCardProps[] => {
return coupons.filter(coupon => isCouponUsable(coupon, totalAmount))
}
/**
* 过滤不可用优惠券
*/
export const filterUnusableCoupons = (
coupons: CouponCardProps[],
totalAmount: number
): CouponCardProps[] => {
return coupons.filter(coupon => !isCouponUsable(coupon, totalAmount))
}

103
src/utils/customerStatus.ts Normal file
View File

@@ -0,0 +1,103 @@
/**
* 客户状态管理工具函数
*/
// 客户状态类型定义
export type CustomerStatus = 'all' | 'pending' | 'signed' | 'cancelled';
// 客户状态配置
export const CUSTOMER_STATUS_CONFIG = {
all: {
label: '全部',
color: '#666666',
tagType: 'default' as const
},
pending: {
label: '跟进中',
color: '#ff8800',
tagType: 'warning' as const
},
signed: {
label: '已签约',
color: '#52c41a',
tagType: 'success' as const
},
cancelled: {
label: '已取消',
color: '#999999',
tagType: 'default' as const
}
};
/**
* 获取状态文本
*/
export const getStatusText = (status: CustomerStatus): string => {
return CUSTOMER_STATUS_CONFIG[status]?.label || '';
};
/**
* 获取状态标签类型
*/
export const getStatusTagType = (status: CustomerStatus) => {
return CUSTOMER_STATUS_CONFIG[status]?.tagType || 'default';
};
/**
* 获取状态颜色
*/
export const getStatusColor = (status: CustomerStatus): string => {
return CUSTOMER_STATUS_CONFIG[status]?.color || '#666666';
};
/**
* 获取所有状态选项
*/
export const getStatusOptions = () => {
return Object.entries(CUSTOMER_STATUS_CONFIG).map(([value, config]) => ({
value: value as CustomerStatus,
label: config.label
}));
};
/**
* 将数字状态映射为字符串状态
*/
export const mapApplyStatusToCustomerStatus = (applyStatus: number): CustomerStatus => {
switch (applyStatus) {
case 10:
return 'pending'; // 跟进中
case 20:
return 'signed'; // 已签约
case 30:
return 'cancelled'; // 已取消
default:
return 'pending'; // 默认为跟进中
}
};
/**
* 将字符串状态映射为数字状态
*/
export const mapCustomerStatusToApplyStatus = (customerStatus: CustomerStatus): number | undefined => {
switch (customerStatus) {
case 'pending':
return 10; // 跟进中
case 'signed':
return 20; // 已签约
case 'cancelled':
return 30; // 已取消
case 'all':
return undefined; // 全部,不筛选
default:
return undefined;
}
};
/**
* 临时函数:生成随机状态(实际项目中应该删除,从数据库获取真实状态)
*/
export const getRandomStatus = (): CustomerStatus => {
const statuses: CustomerStatus[] = ['pending', 'signed', 'cancelled'];
return statuses[Math.floor(Math.random() * statuses.length)];
};

126
src/utils/dateUtils.ts Normal file
View File

@@ -0,0 +1,126 @@
/**
* 日期格式化工具函数
* 用于处理各种日期格式转换
*/
/**
* 格式化日期为数据库格式 YYYY-MM-DD HH:mm:ss
* @param dateStr 输入的日期字符串,支持多种格式
* @returns 数据库格式的日期字符串
*/
export const formatDateForDatabase = (dateStr: string): string => {
if (!dateStr) return ''
let parts: string[] = []
// 处理不同的日期格式
if (dateStr.includes('/')) {
// 处理 YYYY/MM/DD 或 YYYY/M/D 格式
parts = dateStr.split('/')
} else if (dateStr.includes('-')) {
// 处理 YYYY-MM-DD 或 YYYY-M-D 格式
parts = dateStr.split('-')
} else {
return dateStr
}
if (parts.length !== 3) return dateStr
const year = parts[0]
const month = parts[1].padStart(2, '0')
const day = parts[2].padStart(2, '0')
return `${year}-${month}-${day} 00:00:00`
}
/**
* 从数据库格式提取日期部分用于Calendar组件显示
* @param dateTimeStr 数据库格式的日期时间字符串
* @returns Calendar组件需要的格式 (YYYY-M-D)
*/
export const extractDateForCalendar = (dateTimeStr: string): string => {
if (!dateTimeStr) return ''
// 处理不同的输入格式
let dateStr = ''
if (dateTimeStr.includes(' ')) {
// 从 "YYYY-MM-DD HH:mm:ss" 格式中提取日期部分
dateStr = dateTimeStr.split(' ')[0]
} else {
dateStr = dateTimeStr
}
// 转换为Calendar组件需要的格式 (YYYY-M-D)
if (dateStr.includes('-')) {
const parts = dateStr.split('-')
if (parts.length === 3) {
const year = parts[0]
const month = parseInt(parts[1]).toString() // 去掉前导0
const day = parseInt(parts[2]).toString() // 去掉前导0
return `${year}-${month}-${day}`
}
}
return dateStr
}
/**
* 格式化日期为用户友好的显示格式 YYYY-MM-DD
* @param dateStr 输入的日期字符串
* @returns 用户友好的日期格式
*/
export const formatDateForDisplay = (dateStr: string): string => {
if (!dateStr) return ''
// 如果是数据库格式,先提取日期部分
let dateOnly = dateStr
if (dateStr.includes(' ')) {
dateOnly = dateStr.split(' ')[0]
}
// 如果已经是标准格式,直接返回
if (/^\d{4}-\d{2}-\d{2}$/.test(dateOnly)) {
return dateOnly
}
// 处理其他格式
let parts: string[] = []
if (dateOnly.includes('/')) {
parts = dateOnly.split('/')
} else if (dateOnly.includes('-')) {
parts = dateOnly.split('-')
} else {
return dateStr
}
if (parts.length !== 3) return dateStr
const year = parts[0]
const month = parts[1].padStart(2, '0')
const day = parts[2].padStart(2, '0')
return `${year}-${month}-${day}`
}
/**
* 获取当前日期的字符串格式
* @param format 'database' | 'display' | 'calendar'
* @returns 格式化的当前日期
*/
export const getCurrentDate = (format: 'database' | 'display' | 'calendar' = 'display'): string => {
const now = new Date()
const year = now.getFullYear()
const month = now.getMonth() + 1
const day = now.getDate()
switch (format) {
case 'database':
return `${year}-${month.toString().padStart(2, '0')}-${day.toString().padStart(2, '0')} 00:00:00`
case 'display':
return `${year}-${month.toString().padStart(2, '0')}-${day.toString().padStart(2, '0')}`
case 'calendar':
return `${year}-${month}-${day}`
default:
return `${year}-${month.toString().padStart(2, '0')}-${day.toString().padStart(2, '0')}`
}
}

110
src/utils/domain.ts Normal file
View File

@@ -0,0 +1,110 @@
// 解析域名结构
export function getHost(): any {
const host = window.location.host;
return host.split('.');
}
// 是否https
export function isHttps() {
const protocol = window.location.protocol;
if (protocol == 'https:') {
return true;
}
return false;
}
/**
* 获取原始域名
* @return http://www.domain.com
*/
export function getOriginDomain(): string {
return window.origin;
}
/**
* 域名的第一部分
* 获取tenantId
* @return 10140
*/
export function getDomainPart1(): any {
const split = getHost();
if (split[0] == '127') {
return undefined;
}
if (typeof (split[0])) {
return split[0];
}
return undefined;
}
/**
* 通过解析泛域名获取租户ID
* https://10140.wsdns.cn
* @return 10140
*/
export function getTenantId() {
let tenantId = localStorage.getItem('TenantId');
if(getDomainPart1()){
tenantId = getDomainPart1();
return tenantId;
}
return tenantId;
}
/**
* 获取根域名
* hostname
*/
export function getHostname(): string {
return window.location.hostname;
}
/**
* 获取域名
* @return https://www.domain.com
*/
export function getDomain(): string {
return window.location.protocol + '//www.' + getRootDomain();
}
/**
* 获取根域名
* abc.com
*/
export function getRootDomain(): string {
const split = getHost();
return split[split.length - 2] + '.' + split[split.length - 1];
}
/**
* 获取二级域名
* @return abc.com
*/
export function getSubDomainPath(): string {
const split = getHost();
if (split.length == 2) {
return '';
}
return split[split.length - 3];
}
/**
* 获取产品标识
* @return 10048
*/
export function getProductCode(): string | null {
const subDomain = getSubDomainPath();
if (subDomain == undefined) {
return null;
}
const split = subDomain.split('-');
return split[0];
}
/**
* 控制台域名
*/
export function navSubDomain(path: string): string {
return `${window.location.protocol}//${path}.${getRootDomain()}`;
}

302
src/utils/errorHandler.ts Normal file
View File

@@ -0,0 +1,302 @@
import Taro from '@tarojs/taro';
// 定义本地的RequestError类避免循环依赖
export class RequestError extends Error {
public type: string;
public code?: number;
public data?: any;
constructor(message: string, type: string, code?: number, data?: any) {
super(message);
this.name = 'RequestError';
this.type = type;
this.code = code;
this.data = data;
}
}
// 错误类型枚举
export enum ErrorType {
NETWORK_ERROR = 'NETWORK_ERROR',
TIMEOUT_ERROR = 'TIMEOUT_ERROR',
BUSINESS_ERROR = 'BUSINESS_ERROR',
AUTH_ERROR = 'AUTH_ERROR',
UNKNOWN_ERROR = 'UNKNOWN_ERROR'
}
// 错误级别枚举
export enum ErrorLevel {
INFO = 'info',
WARNING = 'warning',
ERROR = 'error',
FATAL = 'fatal'
}
// 错误信息接口
export interface ErrorInfo {
message: string;
level: ErrorLevel;
type: string;
stack?: string;
extra?: any;
timestamp: number;
userId?: string;
page?: string;
}
/**
* 全局错误处理器
*/
class GlobalErrorHandler {
private static instance: GlobalErrorHandler;
private errorQueue: ErrorInfo[] = [];
private maxQueueSize = 50;
private constructor() {
this.setupGlobalErrorHandlers();
}
public static getInstance(): GlobalErrorHandler {
if (!GlobalErrorHandler.instance) {
GlobalErrorHandler.instance = new GlobalErrorHandler();
}
return GlobalErrorHandler.instance;
}
/**
* 设置全局错误处理器
*/
private setupGlobalErrorHandlers() {
// 捕获未处理的Promise rejection
if (typeof window !== 'undefined') {
window.addEventListener('unhandledrejection', (event) => {
this.handleError(event.reason, ErrorLevel.ERROR, 'UnhandledPromiseRejection');
event.preventDefault();
});
}
}
/**
* 处理错误
*/
public handleError(
error: any,
level: ErrorLevel = ErrorLevel.ERROR,
type: string = 'Unknown',
extra?: any
) {
const errorInfo = this.createErrorInfo(error, level, type, extra);
// 添加到错误队列
this.addToQueue(errorInfo);
// 根据错误级别决定处理方式
switch (level) {
case ErrorLevel.FATAL:
this.handleFatalError(errorInfo);
break;
case ErrorLevel.ERROR:
this.handleNormalError(errorInfo);
break;
case ErrorLevel.WARNING:
this.handleWarning(errorInfo);
break;
case ErrorLevel.INFO:
this.handleInfo(errorInfo);
break;
}
// 上报错误
this.reportError(errorInfo);
}
/**
* 创建错误信息对象
*/
private createErrorInfo(
error: any,
level: ErrorLevel,
type: string,
extra?: any
): ErrorInfo {
let message = '未知错误';
let stack: string | undefined;
if (error instanceof Error) {
message = error.message;
stack = error.stack;
} else if (error instanceof RequestError) {
message = error.message;
type = error.type;
extra = { ...extra, code: error.code, data: error.data };
} else if (typeof error === 'string') {
message = error;
} else if (error && typeof error === 'object') {
message = error.message || error.errMsg || JSON.stringify(error);
}
return {
message,
level,
type,
stack,
extra,
timestamp: Date.now(),
userId: Taro.getStorageSync('UserId') || undefined,
page: this.getCurrentPage()
};
}
/**
* 获取当前页面路径
*/
private getCurrentPage(): string {
try {
const pages = Taro.getCurrentPages();
const currentPage = pages[pages.length - 1];
return currentPage?.route || 'unknown';
} catch {
return 'unknown';
}
}
/**
* 添加到错误队列
*/
private addToQueue(errorInfo: ErrorInfo) {
this.errorQueue.push(errorInfo);
// 保持队列大小
if (this.errorQueue.length > this.maxQueueSize) {
this.errorQueue.shift();
}
}
/**
* 处理致命错误
*/
private handleFatalError(errorInfo: ErrorInfo) {
console.error('Fatal Error:', errorInfo);
Taro.showModal({
title: '严重错误',
content: '应用遇到严重错误,需要重启',
showCancel: false,
confirmText: '重启应用',
success: () => {
Taro.reLaunch({ url: '/pages/index/index' });
}
});
}
/**
* 处理普通错误
*/
private handleNormalError(errorInfo: ErrorInfo) {
console.error('Error:', errorInfo);
// 根据错误类型显示不同的提示
let title = '操作失败';
if (errorInfo.type === ErrorType.NETWORK_ERROR) {
title = '网络连接失败';
} else if (errorInfo.type === ErrorType.TIMEOUT_ERROR) {
title = '请求超时';
} else if (errorInfo.type === ErrorType.AUTH_ERROR) {
title = '认证失败';
}
Taro.showToast({
title: errorInfo.message || title,
icon: 'error',
duration: 2000
});
}
/**
* 处理警告
*/
private handleWarning(errorInfo: ErrorInfo) {
console.warn('Warning:', errorInfo);
// 警告通常不需要用户交互,只记录日志
}
/**
* 处理信息
*/
private handleInfo(errorInfo: ErrorInfo) {
console.info('Info:', errorInfo);
}
/**
* 上报错误到服务器
*/
private reportError(errorInfo: ErrorInfo) {
try {
// 这里可以实现错误上报逻辑
// 例如发送到后端日志系统、第三方监控服务等
// 示例:发送到后端
// request.post('/api/error/report', errorInfo).catch(() => {
// // 上报失败也不要影响用户体验
// });
// 开发环境下打印详细信息
if (process.env.NODE_ENV === 'development') {
console.group('🚨 Error Report');
console.log('Message:', errorInfo.message);
console.log('Level:', errorInfo.level);
console.log('Type:', errorInfo.type);
console.log('Page:', errorInfo.page);
console.log('UserId:', errorInfo.userId);
console.log('Timestamp:', new Date(errorInfo.timestamp).toLocaleString());
if (errorInfo.stack) {
console.log('Stack:', errorInfo.stack);
}
if (errorInfo.extra) {
console.log('Extra:', errorInfo.extra);
}
console.groupEnd();
}
} catch (reportError) {
console.error('Failed to report error:', reportError);
}
}
/**
* 获取错误队列
*/
public getErrorQueue(): ErrorInfo[] {
return [...this.errorQueue];
}
/**
* 清空错误队列
*/
public clearErrorQueue() {
this.errorQueue = [];
}
}
// 导出单例实例
export const errorHandler = GlobalErrorHandler.getInstance();
// 便捷方法
export const handleError = (error: any, level?: ErrorLevel, type?: string, extra?: any) => {
errorHandler.handleError(error, level, type, extra);
};
export const handleFatalError = (error: any, extra?: any) => {
errorHandler.handleError(error, ErrorLevel.FATAL, 'FatalError', extra);
};
export const handleWarning = (error: any, extra?: any) => {
errorHandler.handleError(error, ErrorLevel.WARNING, 'Warning', extra);
};
export const handleInfo = (message: string, extra?: any) => {
errorHandler.handleError(message, ErrorLevel.INFO, 'Info', extra);
};
export default errorHandler;

296
src/utils/invite.ts Normal file
View File

@@ -0,0 +1,296 @@
import Taro from '@tarojs/taro'
import {bindRefereeRelation} from "@/api/invite";
/**
* 邀请参数接口
*/
export interface InviteParams {
inviter?: string;
source?: string;
t?: string;
}
/**
* 解析小程序启动参数中的邀请信息
*/
export function parseInviteParams(options: any): InviteParams | null {
try {
console.log('解析邀请参数:', options)
// 优先从 query.scene 中解析邀请信息
if (options.query && options.query.scene) {
const sceneStr = typeof options.query.scene === 'string' ? options.query.scene : String(options.query.scene)
console.log('从 query.scene 解析:', sceneStr)
// 处理 uid_xxxxx 格式的参数
if (sceneStr.startsWith('uid_')) {
const uid = sceneStr.replace('uid_', '')
if (uid) {
return {
inviter: uid,
source: 'qrcode',
t: String(Date.now())
}
}
}
// 处理 key=value&key=value 格式的参数
if (sceneStr.includes('=')) {
const params: InviteParams = {}
const pairs = sceneStr.split('&')
pairs.forEach((pair: string) => {
const [key, value] = pair.split('=')
if (key && value) {
switch (key) {
case 'inviter':
case 'uid':
params.inviter = decodeURIComponent(value)
break
case 'source':
params.source = decodeURIComponent(value)
break
case 't':
params.t = decodeURIComponent(value)
break
}
}
})
if (params.inviter) {
return params
}
}
}
// 从 scene 参数中解析邀请信息(兼容旧版本)
if (options.scene) {
const sceneStr = typeof options.scene === 'string' ? options.scene : String(options.scene)
console.log('从 scene 解析:', sceneStr)
// 处理 uid_xxxxx 格式的参数
if (sceneStr.startsWith('uid_')) {
const uid = sceneStr.replace('uid_', '')
if (uid) {
return {
inviter: uid,
source: 'qrcode',
t: String(Date.now())
}
}
}
// 处理 key=value&key=value 格式的参数
if (sceneStr.includes('=')) {
const params: InviteParams = {}
const pairs = sceneStr.split('&')
pairs.forEach((pair: string) => {
const [key, value] = pair.split('=')
if (key && value) {
switch (key) {
case 'inviter':
case 'uid':
params.inviter = decodeURIComponent(value)
break
case 'source':
params.source = decodeURIComponent(value)
break
case 't':
params.t = decodeURIComponent(value)
break
}
}
})
if (params.inviter) {
return params
}
}
}
// 从 query 参数中解析邀请信息(兼容旧版本)
if (options.referrer) {
return {
inviter: options.referrer,
source: 'link'
}
}
return null
} catch (error) {
console.error('解析邀请参数失败:', error)
return null
}
}
/**
* 保存邀请信息到本地存储
*/
export function saveInviteParams(params: InviteParams) {
try {
Taro.setStorageSync('invite_params', {
...params,
timestamp: Date.now()
})
console.log('邀请参数已保存:', params)
} catch (error) {
console.error('保存邀请参数失败:', error)
}
}
/**
* 获取本地存储的邀请信息
*/
export function getStoredInviteParams(): InviteParams | null {
try {
const stored = Taro.getStorageSync('invite_params')
if (stored && stored.inviter) {
// 检查是否过期24小时
const now = Date.now()
const expireTime = 24 * 60 * 60 * 1000 // 24小时
if (now - stored.timestamp < expireTime) {
return {
inviter: stored.inviter,
source: stored.source || 'unknown',
t: stored.t
}
} else {
// 过期则清除
clearInviteParams()
}
}
return null
} catch (error) {
console.error('获取邀请参数失败:', error)
return null
}
}
/**
* 清除本地存储的邀请信息
*/
export function clearInviteParams() {
try {
Taro.removeStorageSync('invite_params')
console.log('邀请参数已清除')
} catch (error) {
console.error('清除邀请参数失败:', error)
}
}
/**
* 处理邀请关系建立
*/
export async function handleInviteRelation(userId: number): Promise<boolean> {
try {
const inviteParams = getStoredInviteParams()
if (!inviteParams || !inviteParams.inviter) {
return false
}
const inviterId = parseInt(inviteParams.inviter)
if (isNaN(inviterId) || inviterId === userId) {
// 邀请人ID无效或自己邀请自己
clearInviteParams()
return false
}
// 建立邀请关系
await bindRefereeRelation({
dealerId: inviterId,
userId: userId,
source: inviteParams.source || 'unknown',
scene: `inviter=${inviterId}&source=${inviteParams.source}&t=${inviteParams.t}`
})
// 清除本地存储的邀请参数
clearInviteParams()
console.log(`邀请关系建立成功: ${inviterId} -> ${userId}`)
return true
} catch (error) {
console.error('建立邀请关系失败:', error)
return false
}
}
/**
* 检查是否有待处理的邀请
*/
export function hasPendingInvite(): boolean {
const params = getStoredInviteParams()
return !!(params && params.inviter)
}
/**
* 获取邀请来源的显示名称
*/
export function getSourceDisplayName(source: string): string {
const sourceMap: Record<string, string> = {
'qrcode': '小程序码',
'link': '分享链接',
'share': '好友分享',
'poster': '海报分享',
'unknown': '未知来源'
}
return sourceMap[source] || source
}
/**
* 验证邀请码格式
*/
export function validateInviteCode(scene: string): boolean {
try {
if (!scene) return false
// 检查是否包含必要的参数
const hasInviter = scene.includes('inviter=')
const hasSource = scene.includes('source=')
return hasInviter && hasSource
} catch (error) {
return false
}
}
/**
* 生成邀请场景值
*/
export function generateInviteScene(inviterId: number, source: string): string {
const timestamp = Date.now()
return `inviter=${inviterId}&source=${source}&t=${timestamp}`
}
/**
* 统计邀请来源
*/
export function trackInviteSource(source: string, inviterId?: number) {
try {
// 记录邀请来源统计
const trackData = {
source,
inviterId,
timestamp: Date.now(),
userAgent: Taro.getSystemInfoSync()
}
// 可以发送到统计服务
console.log('邀请来源统计:', trackData)
// 暂存到本地,后续可批量上报
const existingTracks = Taro.getStorageSync('invite_tracks') || []
existingTracks.push(trackData)
// 只保留最近100条记录
if (existingTracks.length > 100) {
existingTracks.splice(0, existingTracks.length - 100)
}
Taro.setStorageSync('invite_tracks', existingTracks)
} catch (error) {
console.error('统计邀请来源失败:', error)
}
}

31
src/utils/jsonUtils.ts Normal file
View File

@@ -0,0 +1,31 @@
/**
* 判断字符串是否为有效的JSON格式
* @param str 要检测的字符串
* @returns boolean
*/
export function isValidJSON(str: string): boolean {
if (typeof str !== 'string' || str.trim() === '') {
return false;
}
try {
JSON.parse(str);
return true;
} catch (error) {
return false;
}
}
/**
* 安全解析JSON失败时返回默认值
* @param str JSON字符串
* @param defaultValue 默认值
* @returns 解析结果或默认值
*/
export function safeJSONParse<T>(str: string, defaultValue: T): T {
try {
return JSON.parse(str);
} catch (error) {
return defaultValue;
}
}

344
src/utils/payment.ts Normal file
View File

@@ -0,0 +1,344 @@
import Taro from '@tarojs/taro';
import { createOrder, WxPayResult } from '@/api/shop/shopOrder';
import { OrderCreateRequest } from '@/api/shop/shopOrder/model';
/**
* 支付类型枚举
*/
export enum PaymentType {
BALANCE = 0, // 余额支付
WECHAT = 1, // 微信支付
ALIPAY = 3, // 支付宝支付
}
/**
* 支付结果回调
*/
export interface PaymentCallback {
onSuccess?: () => void;
onError?: (error: string) => void;
onComplete?: () => void;
}
/**
* 统一支付处理类
*/
export class PaymentHandler {
/**
* 执行支付
* @param orderData 订单数据
* @param paymentType 支付类型
* @param callback 回调函数
*/
static async pay(
orderData: OrderCreateRequest,
paymentType: PaymentType,
callback?: PaymentCallback
): Promise<void> {
Taro.showLoading({ title: '支付中...' });
try {
// 设置支付类型
orderData.payType = paymentType;
console.log('创建订单请求:', orderData);
// 创建订单
const result = await createOrder(orderData);
console.log('订单创建结果:', result);
if (!result) {
throw new Error('创建订单失败');
}
// 验证订单创建结果
if (!result.orderNo) {
throw new Error('订单号获取失败');
}
let paymentSuccess = false;
// 根据支付类型处理
switch (paymentType) {
case PaymentType.WECHAT:
await this.handleWechatPay(result);
paymentSuccess = true;
break;
case PaymentType.BALANCE:
paymentSuccess = await this.handleBalancePay(result);
break;
case PaymentType.ALIPAY:
await this.handleAlipay(result);
paymentSuccess = true;
break;
default:
throw new Error('不支持的支付方式');
}
// 只有确认支付成功才显示成功提示和跳转
if (paymentSuccess) {
console.log('支付成功,订单号:', result.orderNo);
Taro.showToast({
title: '支付成功',
icon: 'success'
});
callback?.onSuccess?.();
// 跳转到订单页面
setTimeout(() => {
Taro.navigateTo({ url: '/user/order/order' });
}, 2000);
} else {
throw new Error('支付未完成');
}
} catch (error: any) {
console.error('支付失败:', error);
// 获取详细错误信息
const errorMessage = this.getErrorMessage(error);
Taro.showToast({
title: errorMessage,
icon: 'error'
});
// 标记错误已处理,避免上层重复处理
error.handled = true;
callback?.onError?.(errorMessage);
// 重新抛出错误,让上层知道支付失败
throw error;
} finally {
Taro.hideLoading();
callback?.onComplete?.();
}
}
/**
* 处理微信支付
*/
private static async handleWechatPay(result: WxPayResult): Promise<void> {
console.log('处理微信支付:', result);
if (!result) {
throw new Error('微信支付参数错误');
}
// 验证微信支付必要参数
if (!result.timeStamp || !result.nonceStr || !result.package || !result.paySign) {
throw new Error('微信支付参数不完整');
}
try {
await Taro.requestPayment({
timeStamp: result.timeStamp,
nonceStr: result.nonceStr,
package: result.package,
signType: result.signType as any, // 类型转换因为微信支付的signType是字符串
paySign: result.paySign,
});
console.log('微信支付成功');
} catch (payError: any) {
console.error('微信支付失败:', payError);
// 处理微信支付特定错误
if (payError.errMsg) {
if (payError.errMsg.includes('cancel')) {
throw new Error('用户取消支付');
} else if (payError.errMsg.includes('fail')) {
throw new Error('微信支付失败,请重试');
}
}
throw new Error('微信支付失败');
}
}
/**
* 处理余额支付
*/
private static async handleBalancePay(result: any): Promise<boolean> {
console.log('处理余额支付:', result);
if (!result || !result.orderNo) {
throw new Error('余额支付参数错误');
}
// 检查支付状态 - 根据后端返回的字段调整
if (result.payStatus === false || result.payStatus === 0 || result.payStatus === '0') {
throw new Error('余额不足或支付失败');
}
// 检查订单状态 - 1表示已付款
if (result.orderStatus !== undefined && result.orderStatus !== 1) {
throw new Error('订单状态异常,支付可能未成功');
}
// 验证实际扣款金额
if (result.payPrice !== undefined) {
const payPrice = parseFloat(result.payPrice);
if (payPrice <= 0) {
throw new Error('支付金额异常');
}
}
// 如果有错误信息字段,检查是否有错误
if (result.error || result.errorMsg) {
throw new Error(result.error || result.errorMsg);
}
console.log('余额支付验证通过');
return true;
}
/**
* 处理支付宝支付
*/
private static async handleAlipay(_result: any): Promise<void> {
// 支付宝支付逻辑,根据实际情况实现
throw new Error('支付宝支付暂未实现');
}
/**
* 获取详细错误信息
*/
private static getErrorMessage(error: any): string {
if (!error.message) {
return '支付失败,请重试';
}
const message = error.message;
// 余额相关错误
if (message.includes('余额不足') || message.includes('balance')) {
return '账户余额不足,请充值后重试';
}
// 优惠券相关错误
if (message.includes('优惠券') || message.includes('coupon')) {
return '优惠券使用失败,请重新选择';
}
// 库存相关错误
if (message.includes('库存') || message.includes('stock')) {
return '商品库存不足,请减少购买数量';
}
// 地址相关错误
if (message.includes('地址') || message.includes('address')) {
return '收货地址信息有误,请重新选择';
}
// 订单相关错误
if (message.includes('订单') || message.includes('order')) {
return '订单创建失败,请重试';
}
// 网络相关错误
if (message.includes('网络') || message.includes('network') || message.includes('timeout')) {
return '网络连接异常,请检查网络后重试';
}
// 微信支付相关错误
if (message.includes('微信') || message.includes('wechat') || message.includes('wx')) {
return '微信支付失败,请重试';
}
// 返回原始错误信息
return message;
}
}
/**
* 快捷支付方法
*/
export const quickPay = {
/**
* 微信支付
*/
wechat: (orderData: OrderCreateRequest, callback?: PaymentCallback) => {
return PaymentHandler.pay(orderData, PaymentType.WECHAT, callback);
},
/**
* 余额支付
*/
balance: (orderData: OrderCreateRequest, callback?: PaymentCallback) => {
return PaymentHandler.pay(orderData, PaymentType.BALANCE, callback);
},
/**
* 支付宝支付
*/
alipay: (orderData: OrderCreateRequest, callback?: PaymentCallback) => {
return PaymentHandler.pay(orderData, PaymentType.ALIPAY, callback);
}
};
/**
* 构建单商品订单数据
*/
export function buildSingleGoodsOrder(
goodsId: number,
quantity: number = 1,
addressId?: number,
options?: {
comments?: string;
deliveryType?: number;
couponId?: any;
selfTakeMerchantId?: number;
skuId?: number;
specInfo?: string;
buyerRemarks?: string;
}
): OrderCreateRequest {
return {
goodsItems: [
{
goodsId,
quantity,
skuId: options?.skuId,
specInfo: options?.specInfo
}
],
addressId,
payType: PaymentType.WECHAT, // 默认微信支付会被PaymentHandler覆盖
comments: options?.buyerRemarks || options?.comments || '',
deliveryType: options?.deliveryType || 0,
couponId: options?.couponId,
selfTakeMerchantId: options?.selfTakeMerchantId
};
}
/**
* 构建购物车订单数据
*/
export function buildCartOrder(
cartItems: Array<{ goodsId: number; quantity: number }>,
addressId?: number,
options?: {
comments?: string;
deliveryType?: number;
couponId?: number;
selfTakeMerchantId?: number;
}
): OrderCreateRequest {
return {
goodsItems: cartItems.map(item => ({
goodsId: item.goodsId,
quantity: item.quantity
})),
addressId,
payType: PaymentType.WECHAT, // 默认微信支付会被PaymentHandler覆盖
comments: options?.comments || '购物车下单',
deliveryType: options?.deliveryType || 0,
couponId: options?.couponId,
selfTakeMerchantId: options?.selfTakeMerchantId
};
}

425
src/utils/request.ts Normal file
View File

@@ -0,0 +1,425 @@
import Taro from '@tarojs/taro'
import { BaseUrl, TenantId } from "@/config/app";
// 请求配置接口
interface RequestConfig {
url: string;
method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
data?: any;
header?: Record<string, string>;
timeout?: number;
retry?: number;
showLoading?: boolean;
showError?: boolean;
returnRaw?: boolean; // 是否返回原始响应数据
}
// API响应接口
interface ApiResponse<T = any> {
code: number;
message?: string;
data?: T;
}
// 错误类型枚举
enum ErrorType {
NETWORK_ERROR = 'NETWORK_ERROR',
TIMEOUT_ERROR = 'TIMEOUT_ERROR',
BUSINESS_ERROR = 'BUSINESS_ERROR',
AUTH_ERROR = 'AUTH_ERROR',
UNKNOWN_ERROR = 'UNKNOWN_ERROR'
}
// 自定义错误类
class RequestError extends Error {
public type: ErrorType;
public code?: number;
public data?: any;
constructor(message: string, type: ErrorType, code?: number, data?: any) {
super(message);
this.name = 'RequestError';
this.type = type;
this.code = code;
this.data = data;
}
}
// 请求配置
const DEFAULT_CONFIG = {
timeout: 10000, // 10秒超时
retry: 2, // 重试2次
showLoading: false,
showError: true
};
let baseUrl = Taro.getStorageSync('ApiUrl') || BaseUrl;
// 开发环境配置
if (process.env.NODE_ENV === 'development') {
// baseUrl = 'http://localhost:9200/api'
}
// 请求拦截器
const requestInterceptor = (config: RequestConfig): RequestConfig => {
// 添加认证token
const token = Taro.getStorageSync('access_token');
const tenantId = Taro.getStorageSync('TenantId') || TenantId;
const defaultHeaders: Record<string, string> = {
'Content-Type': 'application/json',
'TenantId': tenantId
};
if (token) {
defaultHeaders['Authorization'] = token;
}
config.header = { ...defaultHeaders, ...config.header };
// 显示加载提示
if (config.showLoading) {
Taro.showLoading({ title: '加载中...' });
}
return config;
};
// 响应拦截器
const responseInterceptor = <T>(response: any, config: RequestConfig): T => {
// 隐藏加载提示
if (config.showLoading) {
Taro.hideLoading();
}
const { statusCode, data } = response;
// HTTP状态码检查
if (statusCode !== 200) {
throw new RequestError(
`HTTP错误: ${statusCode}`,
ErrorType.NETWORK_ERROR,
statusCode,
data
);
}
// 如果没有数据,抛出错误
if (!data) {
throw new RequestError(
'响应数据为空',
ErrorType.NETWORK_ERROR,
statusCode,
data
);
}
// 业务状态码检查
if (typeof data === 'object' && 'code' in data) {
const apiResponse = data as ApiResponse<T>;
// 成功响应
if (apiResponse.code === 0) {
// 如果配置了返回原始响应,则返回完整响应
if (config.returnRaw) {
return data as T;
}
// 否则返回data部分
return apiResponse.data as T;
}
// 认证错误
if (apiResponse.code === 401 || apiResponse.code === 403) {
handleAuthError();
throw new RequestError(
apiResponse.message || '认证失败',
ErrorType.AUTH_ERROR,
apiResponse.code,
apiResponse.data
);
}
// 业务错误
throw new RequestError(
apiResponse.message || '请求失败',
ErrorType.BUSINESS_ERROR,
apiResponse.code,
apiResponse.data
);
}
// 如果不是标准的API响应格式直接返回数据
return data as T;
};
// 处理认证错误
const handleAuthError = () => {
// 清除本地存储的认证信息
try {
Taro.removeStorageSync('access_token');
Taro.removeStorageSync('User');
Taro.removeStorageSync('UserId');
Taro.removeStorageSync('TenantId');
Taro.removeStorageSync('Phone');
} catch (error) {
console.error('清除认证信息失败:', error);
}
// 显示提示并跳转到登录页
Taro.showToast({
title: '登录已过期,请重新登录',
icon: 'none',
duration: 2000
});
// setTimeout(() => {
// Taro.reLaunch({ url: '/passport/login' });
// }, 2000);
};
// 错误处理
const handleError = (error: RequestError, config: RequestConfig) => {
console.error('请求错误:', error);
if (config.showLoading) {
Taro.hideLoading();
}
if (config.showError) {
let title = '请求失败';
switch (error.type) {
case ErrorType.NETWORK_ERROR:
title = '网络连接失败';
break;
case ErrorType.TIMEOUT_ERROR:
title = '请求超时';
break;
case ErrorType.BUSINESS_ERROR:
title = error.message || '操作失败';
break;
case ErrorType.AUTH_ERROR:
title = '认证失败';
break;
default:
title = '未知错误';
}
Taro.showToast({
title,
icon: 'error',
duration: 2000
});
}
};
// 重试机制
const retryRequest = async <T>(
config: RequestConfig,
retryCount: number = 0
): Promise<T> => {
try {
return await executeRequest<T>(config);
} catch (error) {
const requestError = error as RequestError;
// 如果是认证错误或业务错误,不重试
if (requestError.type === ErrorType.AUTH_ERROR ||
requestError.type === ErrorType.BUSINESS_ERROR) {
throw error;
}
// 如果还有重试次数
if (retryCount < (config.retry || DEFAULT_CONFIG.retry)) {
console.log(`请求失败,正在重试 ${retryCount + 1}/${config.retry || DEFAULT_CONFIG.retry}`);
await new Promise(resolve => setTimeout(resolve, 1000 * (retryCount + 1))); // 递增延迟
return retryRequest<T>(config, retryCount + 1);
}
throw error;
}
};
// 执行请求
const executeRequest = <T>(config: RequestConfig): Promise<T> => {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
reject(new RequestError('请求超时', ErrorType.TIMEOUT_ERROR));
}, config.timeout || DEFAULT_CONFIG.timeout);
Taro.request({
url: config.url,
method: config.method || 'GET',
data: config.data || {},
header: config.header || {},
success: (res) => {
clearTimeout(timer);
try {
const result = responseInterceptor<T>(res, config);
resolve(result);
} catch (error) {
reject(error);
}
},
fail: (err) => {
clearTimeout(timer);
reject(new RequestError(
err.errMsg || '网络请求失败',
ErrorType.NETWORK_ERROR,
undefined,
err
));
}
});
});
};
// 主请求函数
export async function request<T>(options: RequestConfig): Promise<T> {
try {
// 请求拦截
const config = requestInterceptor({ ...DEFAULT_CONFIG, ...options });
// 执行请求(带重试)
const result = await retryRequest<T>(config);
return result;
} catch (error) {
const requestError = error as RequestError;
handleError(requestError, options);
throw requestError;
}
}
// 构建完整URL
const buildUrl = (url: string): string => {
if (url.indexOf('http') === -1) {
return baseUrl + url;
}
return url;
};
// 构建查询参数
const buildQueryString = (params: Record<string, any>): string => {
const queryString = Object.keys(params)
.filter(key => params[key] !== undefined && params[key] !== null)
.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
.join('&');
return queryString ? `?${queryString}` : '';
};
// GET请求 - 返回完整的ApiResult响应适配后台生成的代码
export function get<T>(url: string, params?: any, config?: Partial<RequestConfig>): Promise<T> {
const fullUrl = buildUrl(url) + (params ? buildQueryString(params) : '');
return request<T>({
url: fullUrl,
method: 'GET',
returnRaw: true,
...config
});
}
// POST请求 - 返回完整的ApiResult响应适配后台生成的代码
export function post<T>(url: string, data?: any, config?: Partial<RequestConfig>): Promise<T> {
return request<T>({
url: buildUrl(url),
method: 'POST',
data,
returnRaw: true,
...config
});
}
// PUT请求 - 返回完整的ApiResult响应适配后台生成的代码
export function put<T>(url: string, data?: any, config?: Partial<RequestConfig>): Promise<T> {
return request<T>({
url: buildUrl(url),
method: 'PUT',
data,
returnRaw: true,
...config
});
}
// PATCH请求 - 返回完整的ApiResult响应适配后台生成的代码
export function patch<T>(url: string, data?: any, config?: Partial<RequestConfig>): Promise<T> {
return request<T>({
url: buildUrl(url),
method: 'PATCH',
data,
returnRaw: true,
...config
});
}
// DELETE请求 - 返回完整的ApiResult响应适配后台生成的代码
export function del<T>(url: string, data?: any, config?: Partial<RequestConfig>): Promise<T> {
return request<T>({
url: buildUrl(url),
method: 'DELETE',
data,
returnRaw: true,
...config
});
}
// 便捷方法 - 自动提取data字段用于不需要处理完整ApiResult的场景
export function getData<T>(url: string, params?: any, config?: Partial<RequestConfig>): Promise<T> {
const fullUrl = buildUrl(url) + (params ? buildQueryString(params) : '');
return request<T>({
url: fullUrl,
method: 'GET',
returnRaw: false,
...config
});
}
export function postData<T>(url: string, data?: any, config?: Partial<RequestConfig>): Promise<T> {
return request<T>({
url: buildUrl(url),
method: 'POST',
data,
returnRaw: false,
...config
});
}
export function putData<T>(url: string, data?: any, config?: Partial<RequestConfig>): Promise<T> {
return request<T>({
url: buildUrl(url),
method: 'PUT',
data,
returnRaw: false,
...config
});
}
export function delData<T>(url: string, data?: any, config?: Partial<RequestConfig>): Promise<T> {
return request<T>({
url: buildUrl(url),
method: 'DELETE',
data,
returnRaw: false,
...config
});
}
// 导出错误类型和错误类,供外部使用
export { ErrorType, RequestError };
// 默认导出
export default {
request,
// 主要方法 - 返回完整ApiResult适配后台生成代码
get,
post,
put,
patch,
del,
// 便捷方法 - 自动提取data字段
getData,
postData,
putData,
delData,
ErrorType,
RequestError
};

20
src/utils/server.ts Normal file
View File

@@ -0,0 +1,20 @@
import Taro from '@tarojs/taro';
import {User} from "@/api/system/user/model";
// 模版套餐ID - 请根据实际情况修改
export const TEMPLATE_ID = '10582';
// 服务接口 - 请根据实际情况修改
export const SERVER_API_URL = 'https://server.websoft.top/api';
// export const SERVER_API_URL = 'http://127.0.0.1:8000/api';
/**
* 保存用户信息到本地存储
* @param token
* @param user
*/
export function saveStorageByLoginUser(token: string, user: User) {
Taro.setStorageSync('TenantId',user.tenantId)
Taro.setStorageSync('access_token', token)
Taro.setStorageSync('UserId', user.userId)
Taro.setStorageSync('Phone', user.phone)
Taro.setStorageSync('User', user)
}

39
src/utils/time.ts Normal file
View File

@@ -0,0 +1,39 @@
/**
* 获取当前时间
*/
export function formatCurrentDate() {
// 创建一个Date对象表示当前日期和时间
const now = new Date();
// 获取年、月、日,并进行必要的格式化
const day = String(now.getDate()).padStart(2, '0'); // 获取日,并确保是两位数
const month = String(now.getMonth() + 1).padStart(2, '0'); // 获取月并确保是两位数月份是从0开始的所以要加1
const year = String(now.getFullYear()).slice(-2); // 获取年份的最后两位数字
return `${day}${month}${year}`;
}
/**
* 获取当前时分秒
*/
export function formatHhmmss(){
// 创建一个Date对象表示当前日期和时间
const now = new Date();
// 获取当前的小时
const hour = String(now.getHours()).padStart(2, '0');
// 获取当前的分钟
const minute = String(now.getMinutes()).padStart(2, '0');
// 获取当前的秒数
const second = String(now.getSeconds()).padStart(2, '0');
return `${String(Number(hour) - 8).padStart(2, '0')}${minute}${second}`;
}
/**
* 获取当前小时
*/
export function getCurrentHour() {
const now = new Date();
// 获取当前的小时
const hour = String(now.getHours()).padStart(2, '0');
return `${String(Number(hour) - 8).padStart(2, '0')}`;
}