forked from gxwebsoft/mp-10550
feat(admin): 实现管理员模式切换和扫码登录功能
- 新增管理员模式切换方案,统一管理所有管理员功能 - 实现扫码登录功能,支持用户通过小程序扫描网页端二维码快速登录 - 添加管理员面板组件,集中展示所有管理员功能 - 开发扫码登录按钮和扫描器组件,方便集成到不同页面 - 优化用户界面设计,提高管理员用户的使用体验
This commit is contained in:
5
src/pages/qr-login/index.config.ts
Normal file
5
src/pages/qr-login/index.config.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export default {
|
||||
navigationBarTitleText: '扫码登录',
|
||||
navigationBarTextStyle: 'black',
|
||||
navigationBarBackgroundColor: '#ffffff'
|
||||
}
|
||||
193
src/pages/qr-login/index.tsx
Normal file
193
src/pages/qr-login/index.tsx
Normal file
@@ -0,0 +1,193 @@
|
||||
import React, { useState } from 'react';
|
||||
import { View, Text } from '@tarojs/components';
|
||||
import { NavBar, Card, Divider, Button } from '@nutui/nutui-react-taro';
|
||||
import { ArrowLeft, Scan, Success, Failure, Tips } from '@nutui/icons-react-taro';
|
||||
import Taro from '@tarojs/taro';
|
||||
import QRLoginScanner from '@/components/QRLoginScanner';
|
||||
import { useUser } from '@/hooks/useUser';
|
||||
|
||||
/**
|
||||
* 扫码登录页面
|
||||
*/
|
||||
const QRLoginPage: React.FC = () => {
|
||||
const [loginHistory, setLoginHistory] = useState<any[]>([]);
|
||||
const { getDisplayName } = useUser();
|
||||
|
||||
// 处理扫码成功
|
||||
const handleScanSuccess = (result: any) => {
|
||||
console.log('扫码登录成功:', result);
|
||||
|
||||
// 添加到登录历史
|
||||
const newRecord = {
|
||||
id: Date.now(),
|
||||
time: new Date().toLocaleString(),
|
||||
userInfo: result.userInfo,
|
||||
success: true
|
||||
};
|
||||
setLoginHistory(prev => [newRecord, ...prev.slice(0, 4)]); // 只保留最近5条记录
|
||||
|
||||
// 显示成功提示
|
||||
Taro.showToast({
|
||||
title: '登录确认成功',
|
||||
icon: 'success',
|
||||
duration: 2000
|
||||
});
|
||||
};
|
||||
|
||||
// 处理扫码失败
|
||||
const handleScanError = (error: string) => {
|
||||
console.error('扫码登录失败:', error);
|
||||
|
||||
// 添加到登录历史
|
||||
const newRecord = {
|
||||
id: Date.now(),
|
||||
time: new Date().toLocaleString(),
|
||||
error,
|
||||
success: false
|
||||
};
|
||||
setLoginHistory(prev => [newRecord, ...prev.slice(0, 4)]);
|
||||
};
|
||||
|
||||
// 返回上一页
|
||||
const handleBack = () => {
|
||||
Taro.navigateBack();
|
||||
};
|
||||
|
||||
// 清除历史记录
|
||||
const clearHistory = () => {
|
||||
setLoginHistory([]);
|
||||
Taro.showToast({
|
||||
title: '已清除历史记录',
|
||||
icon: 'success'
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<View className="qr-login-page min-h-screen bg-gray-50">
|
||||
{/* 导航栏 */}
|
||||
<NavBar
|
||||
title="扫码登录"
|
||||
leftShow
|
||||
onBackClick={handleBack}
|
||||
leftText={<ArrowLeft />}
|
||||
className="bg-white"
|
||||
/>
|
||||
|
||||
{/* 主要内容 */}
|
||||
<View className="p-4 space-y-4">
|
||||
{/* 用户信息卡片 */}
|
||||
<Card className="bg-white rounded-lg shadow-sm">
|
||||
<View className="p-4">
|
||||
<View className="flex items-center mb-4">
|
||||
<View className="w-12 h-12 bg-blue-100 rounded-full flex items-center justify-center mr-3">
|
||||
<Scan className="text-blue-500" size="24" />
|
||||
</View>
|
||||
<View>
|
||||
<Text className="text-lg font-bold text-gray-800">
|
||||
{getDisplayName()}
|
||||
</Text>
|
||||
<Text className="text-sm text-gray-500">
|
||||
使用小程序扫码快速登录网页端
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 扫码登录组件 */}
|
||||
<QRLoginScanner
|
||||
onSuccess={handleScanSuccess}
|
||||
onError={handleScanError}
|
||||
buttonText="开始扫码登录"
|
||||
showStatus={true}
|
||||
/>
|
||||
</View>
|
||||
</Card>
|
||||
|
||||
{/* 使用说明 */}
|
||||
<Card className="bg-white rounded-lg shadow-sm">
|
||||
<View className="p-4">
|
||||
<View className="flex items-center mb-3">
|
||||
<Tips className="text-orange-500 mr-2" />
|
||||
<Text className="font-medium text-gray-800">使用说明</Text>
|
||||
</View>
|
||||
<View className="space-y-2 text-sm text-gray-600">
|
||||
<Text className="block">1. 在电脑或其他设备上打开网页端登录页面</Text>
|
||||
<Text className="block">2. 点击"扫码登录"按钮,显示登录二维码</Text>
|
||||
<Text className="block">3. 使用此功能扫描二维码即可快速登录</Text>
|
||||
<Text className="block">4. 扫码成功后,网页端将自动完成登录</Text>
|
||||
</View>
|
||||
</View>
|
||||
</Card>
|
||||
|
||||
{/* 登录历史 */}
|
||||
{loginHistory.length > 0 && (
|
||||
<Card className="bg-white rounded-lg shadow-sm">
|
||||
<View className="p-4">
|
||||
<View className="flex items-center justify-between mb-3">
|
||||
<Text className="font-medium text-gray-800">最近登录记录</Text>
|
||||
<Button
|
||||
size="small"
|
||||
type="default"
|
||||
onClick={clearHistory}
|
||||
className="text-xs"
|
||||
>
|
||||
清除
|
||||
</Button>
|
||||
</View>
|
||||
|
||||
<View className="space-y-3">
|
||||
{loginHistory.map((record, index) => (
|
||||
<View key={record.id}>
|
||||
<View className="flex items-center justify-between">
|
||||
<View className="flex items-center">
|
||||
{record.success ? (
|
||||
<Success className="text-green-500 mr-2" size="16" />
|
||||
) : (
|
||||
<Failure className="text-red-500 mr-2" size="16" />
|
||||
)}
|
||||
<View>
|
||||
<Text className="text-sm text-gray-800">
|
||||
{record.success ? '登录成功' : '登录失败'}
|
||||
</Text>
|
||||
{record.error && (
|
||||
<Text className="text-xs text-red-500">
|
||||
{record.error}
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
<Text className="text-xs text-gray-500">
|
||||
{record.time}
|
||||
</Text>
|
||||
</View>
|
||||
{index < loginHistory.length - 1 && (
|
||||
<Divider className="my-2" />
|
||||
)}
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{/* 安全提示 */}
|
||||
<Card className="bg-yellow-50 border border-yellow-200 rounded-lg">
|
||||
<View className="p-4">
|
||||
<View className="flex items-start">
|
||||
<Tips className="text-yellow-600 mr-2 mt-1" size="16" />
|
||||
<View>
|
||||
<Text className="text-sm font-medium text-yellow-800 mb-1">
|
||||
安全提示
|
||||
</Text>
|
||||
<Text className="text-xs text-yellow-700">
|
||||
请确保只扫描来自官方网站的登录二维码,避免扫描来源不明的二维码,保护账户安全。
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</Card>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default QRLoginPage;
|
||||
@@ -11,6 +11,9 @@ import {TenantId} from "@/config/app";
|
||||
import {useUser} from "@/hooks/useUser";
|
||||
import {useUserData} from "@/hooks/useUserData";
|
||||
import {getStoredInviteParams} from "@/utils/invite";
|
||||
import {useQRLogin} from "@/hooks/useQRLogin";
|
||||
import {useAdminMode} from "@/hooks/useAdminMode";
|
||||
import AdminPanel from "@/components/AdminPanel";
|
||||
|
||||
const UserCard = forwardRef<any, any>((_, ref) => {
|
||||
const {
|
||||
@@ -21,6 +24,15 @@ const UserCard = forwardRef<any, any>((_, ref) => {
|
||||
const [IsLogin, setIsLogin] = useState<boolean>(false)
|
||||
const [userInfo, setUserInfo] = useState<User>()
|
||||
|
||||
// 扫码登录Hook
|
||||
const { startScan, isLoading: isScanLoading } = useQRLogin();
|
||||
|
||||
// 管理员模式Hook
|
||||
const { isAdminMode, toggleAdminMode } = useAdminMode();
|
||||
|
||||
// 管理员面板显示状态
|
||||
const [showAdminPanel, setShowAdminPanel] = useState(false);
|
||||
|
||||
// 下拉刷新
|
||||
const handleRefresh = async () => {
|
||||
await refresh()
|
||||
@@ -168,6 +180,25 @@ const UserCard = forwardRef<any, any>((_, ref) => {
|
||||
})
|
||||
}
|
||||
|
||||
// 处理扫码登录
|
||||
const handleQRLogin = async () => {
|
||||
try {
|
||||
await startScan();
|
||||
} catch (error) {
|
||||
console.error('扫码登录失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 打开管理员面板
|
||||
const handleOpenAdminPanel = () => {
|
||||
setShowAdminPanel(true);
|
||||
};
|
||||
|
||||
// 关闭管理员面板
|
||||
const handleCloseAdminPanel = () => {
|
||||
setShowAdminPanel(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<View className={'header-bg pt-20'}>
|
||||
<View className={'p-4'}>
|
||||
@@ -205,7 +236,31 @@ const UserCard = forwardRef<any, any>((_, ref) => {
|
||||
) : ''}
|
||||
</View>
|
||||
</View>
|
||||
{isAdmin() && <Scan onClick={() => navTo('/user/store/verification', true)} />}
|
||||
{isAdmin() && (
|
||||
<View className="flex items-center space-x-2">
|
||||
{/* 管理员模式切换按钮 */}
|
||||
<View
|
||||
className={`px-3 py-1 rounded-full text-xs font-medium transition-all ${
|
||||
isAdminMode
|
||||
? 'bg-blue-500 text-white'
|
||||
: 'bg-gray-100 text-gray-600 border border-gray-300'
|
||||
}`}
|
||||
onClick={toggleAdminMode}
|
||||
>
|
||||
{isAdminMode ? '管理员' : '普通用户'}
|
||||
</View>
|
||||
|
||||
{/* 管理员功能入口 */}
|
||||
{isAdminMode && (
|
||||
<View
|
||||
className="px-3 py-1 bg-blue-50 border border-blue-200 rounded-full text-xs text-blue-600 font-medium"
|
||||
onClick={handleOpenAdminPanel}
|
||||
>
|
||||
管理面板
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
<View className={'mr-4 text-sm px-3 py-1 text-black border-gray-400 border-solid border-2 rounded-3xl'}
|
||||
onClick={() => navTo('/user/profile/profile', true)}>
|
||||
{'个人资料'}
|
||||
@@ -234,6 +289,14 @@ const UserCard = forwardRef<any, any>((_, ref) => {
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 管理员面板 */}
|
||||
{isAdmin() && (
|
||||
<AdminPanel
|
||||
visible={showAdminPanel}
|
||||
onClose={handleCloseAdminPanel}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user