初步完成merchant和admin不同role的展示内容逻辑2

This commit is contained in:
2026-03-12 16:07:38 +08:00
parent f19dd093bf
commit a81509e1fc
11 changed files with 124 additions and 39 deletions

View File

@@ -26,7 +26,7 @@ export function checkConnection() {
}
// 兼容 supaReady Promise
export const supaReady = Promise.resolve(true)
export const supaReady = supaInstance.hydrateSessionFromStorage()
// 如果有其他需要导出的函数,可以这样导出:
export function initializeSupabase(url: string, key: string) {

View File

@@ -1,5 +1,9 @@
<template>
<view class="layout-root">
<view v-if="!isAuthReady" class="auth-loading-overlay" style="flex: 1; display: flex; align-items: center; justify-content: center; height: 100vh; background-color: #f5f5f5;">
<text style="color: #666; font-size: 16px;">身份鉴权中...</text>
</view>
<template v-else>
<!-- 统一遮罩层 (复刻 CRMEB: 用于所有 Overlay 状态) -->
<view
class="mobile-mask"
@@ -70,6 +74,7 @@
<AdminFooter></AdminFooter>
</view>
</view>
</template>
</view>
</template>
@@ -131,6 +136,7 @@ const SUB_W = 200
// 页面加载状态
const isPageLoading = ref(false)
const isAuthReady = ref(false)
const hasAccess = computed<boolean>(() => {
@@ -391,6 +397,7 @@ let resizeTid: any = null
onMounted(async () => {
await refreshAdminRole()
isAuthReady.value = true
initNavState()
if (props.currentPage != '') {
openRoute(props.currentPage as string)

View File

@@ -62,10 +62,18 @@ import {
openRoute
} from '@/layouts/admin/store/adminNavStore.uts'
import { state, logout } from '@/utils/store.uts'
import { clearAdminRoleCache } from '@/layouts/admin/utils/role.uts'
import { clearAdminRoleCache, getCurrentAdminRole } from '@/layouts/admin/utils/role.uts'
const showUserMenu = ref(false)
const userName = computed((): string => state.userProfile.username || state.userProfile.email || 'admin')
const userName = computed((): string => {
if (state.userProfile?.username != null && state.userProfile!.username != '') return state.userProfile!.username as string
if (state.authUser != null) {
if (state.authUser!.getString('email') != null && state.authUser!.getString('email') != '') return state.authUser!.getString('email') as string
if (state.authUser!.getString('phone') != null && state.authUser!.getString('phone') != '') return state.authUser!.getString('phone') as string
if (state.authUser!.getString('id') != null && state.authUser!.getString('id') != '') return (state.authUser!.getString('id') as string).substring(0, 8)
}
return '未知用户'
})
let hideMenuTimer: number | null = null

View File

@@ -1,4 +1,4 @@
import { state, getCurrentUser } from '@/utils/store.uts'
import { state, getCurrentUser } from '@/utils/store.uts'
import supa from '@/components/supadb/aksupainstance.uts'
/**
@@ -38,26 +38,18 @@ export function getCurrentAdminRole(): string {
}
}
// 2. 缓存兜底:为了刷新页面时立刻渲染,读取最新的 adminRole 缓存
const cachedRole = uni.getStorageSync('adminRole')
if (cachedRole != null && cachedRole != '') {
const normCached = normalizeRole(cachedRole)
if (normCached === 'admin' || normCached === 'merchant') {
return normCached
// 2. Auth Session兜底获取Tab 隔离):
const sessionUser = supa.getSession().user
if (sessionUser != null) {
const meta = sessionUser.get("user_metadata") as UTSJSONObject | null
if (meta != null && meta.getString("role") != null) {
const metaRole = normalizeRole(meta.getString("role"))
if (metaRole === "admin" || metaRole === "merchant") return metaRole
}
}
// 兼容旧的缓存字段以防万一
const oldCached = uni.getStorageSync('admin_role')
if (oldCached != null && oldCached != '') {
const normOld = normalizeRole(oldCached)
if (normOld === 'admin' || normOld === 'merchant') {
return normOld
}
}
console.warn('[AdminRole] 未能获取到有效的管理端角色,准备安全降级...')
return 'unknown'
console.warn("[AdminRole] 未能获取到有效的管理端角色,准备安全降级...")
return "unknown"
}
/**
@@ -67,6 +59,7 @@ export function clearAdminRoleCache(): void {
// 清理 admin 专属
uni.removeStorageSync('adminRole')
uni.removeStorageSync('admin_role')
uni.removeStorageSync('merchant_id')
}
/**
@@ -92,7 +85,7 @@ export async function refreshAdminRole(): Promise<string> {
}
if (finalRole !== 'unknown') {
uni.setStorageSync('adminRole', finalRole)
// uni.setStorageSync('adminRole', finalRole) // 移除缓存耦合,强制按单例会话状态刷新
if (state.userProfile != null) {
state.userProfile!.role = finalRole
}

View File

@@ -1,4 +1,4 @@
<template>
<template>
<view class="product-edit-page">
<view class="page-header">
<view class="back-link" @click="goBack">
@@ -186,7 +186,7 @@ const categoryName = ref('')
onMounted(async () => {
await ensureSupabaseReady()
const mId = uni.getStorageSync('merchant_id') as string | null
const mId = supa.getSession().user?.id as string | null
if (!mId) {
uni.showToast({ title: '商家未获取到信息,请重新登录', icon: 'none' })
return
@@ -346,7 +346,7 @@ async function doSaveProduct(targetStatus: number) {
// 必须获取正确的 merchant_id并且存在
if (!formData.value.merchant_id || formData.value.merchant_id.startsWith('merchant_123')) {
const mId = uni.getStorageSync('merchant_id') as string | null
const mId = supa.getSession().user?.id as string | null
if (!mId) {
uni.showToast({ title: '未获取到商家信息,请重新登录', icon: 'none' })
return

View File

@@ -1,4 +1,4 @@
<template>
<template>
<view class="product-list-page">
<!-- 1. 搜索表单 -->
<view class="search-card">
@@ -197,7 +197,7 @@ async function fetchProducts() {
await ensureSupabaseReady()
// 从本地缓存获取 current merchant_id
const currentMerchantId = uni.getStorageSync('merchant_id') as string | null
const currentMerchantId = supa.getSession().user?.id as string | null
if (!currentMerchantId) {
uni.showToast({ title: '未获取到商家信息,请重新登录', icon: 'none' })

View File

@@ -398,10 +398,10 @@ const handleLogin = async () => {
const currRole = accessData.getString('role')
const currId = accessData.getString('id')
uni.setStorageSync('adminRole', currRole)
// uni.setStorageSync('adminRole', currRole) // 移除本地缓存依赖,强制按单例会话状态
if (currRole === 'merchant') {
uni.setStorageSync('merchant_id', currId)
// uni.setStorageSync('merchant_id', currId) // 移除本地缓存依赖,强制按单例会话状态
} else {
uni.removeStorageSync('merchant_id')
}

35
process.py Normal file
View File

@@ -0,0 +1,35 @@
import re
with open(r'c:\Users\黄镇堡\AppData\Roaming\Code\User\workspaceStorage\51c4c3615fa7807ba1c5a788ca077b83\GitHub.copilot-chat\chat-session-resources\18954447-db08-4bea-a5c4-72e8c9aaf171\call_MHxIMm13YmUwWkdUcjI5b3pueHM__vscode-1773276031647\content.txt', 'r', encoding='utf-8') as f:
text = f.read()
auth_lines = []
bz_lines = []
auth_kw = ['token', 'user', 'role', 'merchant', 'admin', 'auth', 'id']
for line in text.split('\n'):
if not line.strip(): continue
match = re.match(r'^([a-zA-Z]:\\.*?):(\d+):\s*(.*)$', line)
if match:
path = match.group(1)
lineno = match.group(2)
content = match.group(3)
fname = path.split(r'\\')[-1]
is_auth = any(k in content.lower() for k in auth_kw)
output = f'{fname}:{lineno}: {content}'
if is_auth:
auth_lines.append(output)
else:
bz_lines.append(output)
print(f'=== AUTH CACHE USAGES ===')
for l in auth_lines:
if 'ak-req.uts' not in l and 'aksupa.uts' not in l:
print(l[:100])
print(f'\n=== BZ CACHE USAGES (first 20) ===')
for l in bz_lines[:20]:
print(l[:100])

14
search_user_id.py Normal file
View File

@@ -0,0 +1,14 @@
import os
for root, dirs, files in os.walk(r'd:\骅锋\mall'):
if 'node_modules' in root or '.git' in root or 'unpackage' in root: continue
for f in files:
if f.endswith(('.uts', '.uvue')):
path = os.path.join(root, f)
with open(path, 'r', encoding='utf-8') as file:
try:
lines = file.readlines()
for i, l in enumerate(lines):
if 'setStorageSync' in l and 'user_id' in l:
print(f'{path}:{i+1}: {l.strip()}')
except:
pass

View File

@@ -7,6 +7,29 @@ const REFRESH_TOKEN_KEY = 'akreq_refresh_token';
const EXPIRES_AT_KEY = 'akreq_expires_at';
// 优化:用静态变量缓存 token只有 set/clear 时同步 storage
// Web 端(H5)由于多账号联调需要,开启单 Tab 隔离的储流sessionStorage
function setAuthStore(key : string, value : any) {
// #ifdef H5
sessionStorage.setItem(key, String(value));
return;
// #endif
uni.setStorageSync(key, value);
}
function getAuthStore(key : string) : any | null {
// #ifdef H5
return sessionStorage.getItem(key);
// #endif
return uni.getStorageSync(key);
}
function removeAuthStore(key : string) {
// #ifdef H5
sessionStorage.removeItem(key);
return;
// #endif
uni.removeStorageSync(key);
}
let _accessToken : string | null = null;
let _refreshToken : string | null = null;
let _expiresAt : number | null = null;
@@ -16,25 +39,25 @@ export class AkReq {
_accessToken = token;
_refreshToken = refreshToken;
_expiresAt = expiresAt;
uni.setStorageSync(ACCESS_TOKEN_KEY, token);
uni.setStorageSync(REFRESH_TOKEN_KEY, refreshToken);
uni.setStorageSync(EXPIRES_AT_KEY, expiresAt);
setAuthStore(ACCESS_TOKEN_KEY, token);
setAuthStore(REFRESH_TOKEN_KEY, refreshToken);
setAuthStore(EXPIRES_AT_KEY, expiresAt);
}
static getToken() : string | null {
if (_accessToken != null) return _accessToken;
const t = uni.getStorageSync(ACCESS_TOKEN_KEY) as string | null;
const t = getAuthStore(ACCESS_TOKEN_KEY) as string | null;
_accessToken = t;
return t;
}
static getRefreshToken() : string | null {
if (_refreshToken != null) return _refreshToken;
const t = uni.getStorageSync(REFRESH_TOKEN_KEY) as string | null;
const t = getAuthStore(REFRESH_TOKEN_KEY) as string | null;
_refreshToken = t;
return t;
} static getExpiresAt() : number | null {
const val = _expiresAt;
if (val != null) return val;
const t = uni.getStorageSync(EXPIRES_AT_KEY) as number | null;
const t = getAuthStore(EXPIRES_AT_KEY) as number | null;
_expiresAt = t;
return t;
}
@@ -42,9 +65,9 @@ export class AkReq {
_accessToken = null;
_refreshToken = null;
_expiresAt = null;
uni.removeStorageSync(ACCESS_TOKEN_KEY);
uni.removeStorageSync(REFRESH_TOKEN_KEY);
uni.removeStorageSync(EXPIRES_AT_KEY);
removeAuthStore(ACCESS_TOKEN_KEY);
removeAuthStore(REFRESH_TOKEN_KEY);
removeAuthStore(EXPIRES_AT_KEY);
} // 判断 token 是否即将过期提前5分钟刷新
static isTokenExpiring() : boolean {
const expiresAt = this.getExpiresAt();

View File

@@ -17,6 +17,7 @@ export type DeviceState = {
export type State = {
globalNum : number
userProfile ?: UserProfile
authUser ?: UTSJSONObject
isLoggedIn : boolean // 新增字段
deviceState : DeviceState // 新增设备状态
// 如有需要,可增加更多属性
@@ -26,6 +27,7 @@ export type State = {
export const state = reactive({
globalNum: 0,
userProfile: { username: '', email: '' },
authUser: null,
isLoggedIn: false,
deviceState: {
devices: [],
@@ -54,6 +56,7 @@ export async function getCurrentUser() : Promise<UserProfile | null> {
} catch (_) {}
const sessionInfo = supa.getSession()
state.authUser = sessionInfo.user
if (sessionInfo.user == null) {
state.userProfile = { username: '', email: '' }
state.isLoggedIn = false // 未登录
@@ -174,6 +177,7 @@ export function getUserStore() {
return {
getUserId() : string | null {
const sessionInfo = supa.getSession()
state.authUser = sessionInfo.user
return sessionInfo.user?.getString("id") ?? null
},
@@ -184,6 +188,7 @@ export function getUserStore() {
getUserRole() : string | null {
// Default role logic - can be enhanced based on your needs
const sessionInfo = supa.getSession()
state.authUser = sessionInfo.user
if (sessionInfo.user == null) return null
// You can add role detection logic here