- 创建 .editorconfig 文件统一代码风格配置 - 配置 .eslintrc 使用 taro/react 规则集 - 完善 .gitignore 忽略编译产物和敏感文件 - 添加 admin/article/add 页面实现文章管理功能 - 添加 dealer/apply/add 页面实现经销商申请功能 - 添加 dealer/bank/add 页面实现银行卡管理功能 - 添加 dealer/customer/add 页面实现客户管理功能 - 添加 user/address/add 页面实现用户地址管理功能 - 添加 user/chat/message/add 页面实现消息功能 - 添加 user/gift/add 页面实现礼品管理功能 - 配置各页面导航栏标题和样式 - 实现表单验证和数据提交功能 - 集成图片上传和头像选择功能 - 添加日期选择和数据校验逻辑 - 实现编辑和新增模式切换 - 集成用户权限和角色管理功能
297 lines
7.3 KiB
TypeScript
297 lines
7.3 KiB
TypeScript
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)
|
||
}
|
||
}
|