This commit is contained in:
2026-02-03 17:26:23 +08:00
532 changed files with 97923 additions and 20698 deletions

62
utils/authRedirect.uts Normal file
View File

@@ -0,0 +1,62 @@
export type AuthRedirectOptions = {
loginPath?: string
registerPath?: string
fallbackUrl?: string
}
function normalizePath(path: string): string {
if (!path) return ''
return path.startsWith('/') ? path : `/${path}`
}
function safeEncode(url: string): string {
try {
return encodeURIComponent(url)
} catch (_) {
return url
}
}
export function getCurrentPageUrlWithQuery(): string {
const pages = getCurrentPages() as any[]
const currentPage = pages.length > 0 ? pages[pages.length - 1] : null
if (!currentPage) return ''
const route = currentPage.route as string | null
const options = (currentPage.options ?? {}) as Record<string, any>
if (!route) return ''
const path = normalizePath(route)
const keys = Object.keys(options)
if (keys.length === 0) return path
const query = keys
.filter((k) => options[k] != null)
.map((k) => `${k}=${encodeURIComponent(String(options[k]))}`)
.join('&')
return query.length > 0 ? `${path}?${query}` : path
}
export function toLoginWithRedirect(redirectUrl?: string, opts?: AuthRedirectOptions) {
const loginPath = opts?.loginPath ?? '/pages/user/login'
const fallback = opts?.fallbackUrl ?? '/pages/mall/consumer/index'
const target = (redirectUrl != null && redirectUrl.length > 0)
? redirectUrl
: (getCurrentPageUrlWithQuery() || fallback)
uni.navigateTo({
url: `${loginPath}?redirect=${safeEncode(target)}`
})
}
export function toRegisterWithRedirect(redirectUrl?: string, opts?: AuthRedirectOptions) {
const registerPath = opts?.registerPath ?? '/pages/user/register'
const fallback = opts?.fallbackUrl ?? '/pages/mall/consumer/index'
const target = (redirectUrl != null && redirectUrl.length > 0)
? redirectUrl
: (getCurrentPageUrlWithQuery() || fallback)
uni.navigateTo({
url: `${registerPath}?redirect=${safeEncode(target)}`
})
}

View File

@@ -1,4 +1,4 @@
import { supabase, ensureSupabaseReady } from '@/components/supadb/aksupainstance.uts'
import supabase, { supaReady } from '@/components/supadb/aksupainstance.uts'
import type { UserProfile } from '@/types/mall-types.uts'
/**
@@ -8,8 +8,8 @@ import type { UserProfile } from '@/types/mall-types.uts'
*/
export async function ensureUserProfile(sessionUser: UTSJSONObject): Promise<UserProfile | null> {
try {
await ensureSupabaseReady()
await supaReady
// 从 sessionUser 中获取用户ID和邮箱
const userId = sessionUser.getString('id')
const email = sessionUser.getString('email') ?? ''
@@ -19,16 +19,25 @@ export async function ensureUserProfile(sessionUser: UTSJSONObject): Promise<Use
return null
}
// 检查用户是否已存在
// 检查用户是否已存在ak_users 通过 auth_id 关联 auth.users.id
const checkRes = await supabase.from('ak_users')
.select('*', {})
.eq('id', userId)
.eq('auth_id', userId)
.single()
.execute()
console.log('ensureUserProfile check ak_users:', {
status: checkRes.status,
hasData: checkRes.data != null,
hasError: (checkRes as any).error != null,
error: (checkRes as any).error
})
if (checkRes.status >= 200 && checkRes.status < 300 && checkRes.data != null) {
// 用户已存在,返回现有资料
const existingUser = checkRes.data as UTSJSONObject
// 用户已存在,返回现有资料H5 下 checkRes.data 可能是 plain object不一定是 UTSJSONObject
const existingUser = (typeof (checkRes.data as any).getString === 'function')
? (checkRes.data as UTSJSONObject)
: new UTSJSONObject(checkRes.data as any)
return {
id: existingUser.getString('id') ?? '',
username: existingUser.getString('username') ?? '',
@@ -58,8 +67,17 @@ export async function ensureUserProfile(sessionUser: UTSJSONObject): Promise<Use
.single()
.execute()
console.log('ensureUserProfile insert ak_users:', {
status: insertRes.status,
hasData: insertRes.data != null,
hasError: (insertRes as any).error != null,
error: (insertRes as any).error
})
if (insertRes.status >= 200 && insertRes.status < 300 && insertRes.data != null) {
const newUser = insertRes.data as UTSJSONObject
const newUser = (typeof (insertRes.data as any).getString === 'function')
? (insertRes.data as UTSJSONObject)
: new UTSJSONObject(insertRes.data as any)
return {
id: newUser.getString('id') ?? '',
username: newUser.getString('username') ?? '',

View File

@@ -1,4 +1,4 @@
import { supabase as supa, ensureSupabaseReady } from '@/components/supadb/aksupainstance.uts'
import supa, { supaReady } from '@/components/supadb/aksupainstance.uts'
import type { UserProfile, UserStats } from '@/pages/user/types.uts'
import type { DeviceInfo } from '@/pages/sense/types.uts'
import { SenseDataService, type DeviceParams } from '@/pages/sense/senseDataService.uts'
@@ -50,7 +50,7 @@ export const setUserProfile = (profile : UserProfile) => {
// 获取当前用户信息(含补全 profile
export async function getCurrentUser() : Promise<UserProfile | null> {
try {
await ensureSupabaseReady()
await supaReady
} catch (_) {}
const sessionInfo = supa.getSession()

View File

@@ -149,6 +149,20 @@ export function responsiveState() {
}
}
export function goToLogin(redirectUrl?: string | null) {
try {
const target = redirectUrl != null && redirectUrl.length > 0 ? redirectUrl : ''
if (target.length > 0) {
const redirect = encodeURIComponent(target)
uni.navigateTo({ url: `/pages/user/login?redirect=${redirect}` })
} else {
uni.navigateTo({ url: '/pages/user/login' })
}
} catch (e) {
uni.navigateTo({ url: '/pages/user/login' })
}
}
/**
* 兼容 UTS Android 的剪贴板写入
* @param text 要写入剪贴板的文本
@@ -159,3 +173,29 @@ export function setClipboard(text: string): void {
// #endif
}
/**
* 格式化时间,显示为相对时间(如:刚刚,几小时前)
* @param dateStr ISO 格式的日期字符串
* @returns 格式化后的相对时间字符串
*/
export function formatTime(dateStr: string): string {
if (!dateStr) return ''
try {
const date = new Date(dateStr)
const now = new Date()
const diff = now.getTime() - date.getTime()
const hours = Math.floor(diff / (1000 * 60 * 60))
if (hours < 1) {
return '刚刚'
} else if (hours < 24) {
return `${hours}小时前`
} else {
return `${Math.floor(hours / 24)}天前`
}
} catch (e) {
console.error('formatTime error:', e)
return dateStr.replace('T', ' ').split('.')[0]
}
}