|
|
@@ -0,0 +1,520 @@
|
|
|
+import React, { useEffect, useState } from 'react';
|
|
|
+
|
|
|
+import { Icon } from '@iconify/react';
|
|
|
+import mdiAccount from '@iconify-icons/mdi/account';
|
|
|
+import mdiBell from '@iconify-icons/mdi/bell';
|
|
|
+import mdiHeart from '@iconify-icons/mdi/heart';
|
|
|
+import mdiHome from '@iconify-icons/mdi/home';
|
|
|
+import mdiLoading from '@iconify-icons/mdi/loading';
|
|
|
+import mdiSettings from '@iconify-icons/mdi/settings';
|
|
|
+import Button from 'antd/es/button';
|
|
|
+import Card from 'antd/es/card';
|
|
|
+import Space from 'antd/es/space';
|
|
|
+import Typography from 'antd/es/typography';
|
|
|
+import { useTranslation } from 'react-i18next';
|
|
|
+import { useNavigate } from 'react-router-dom';
|
|
|
+
|
|
|
+import multiColorIcon from '@/assets/iconify/multi-color/logo.svg';
|
|
|
+import singleColorIcon from '@/assets/iconify/single-color/home.svg';
|
|
|
+import infoIcon from '@/assets/iconify/single-color/info.svg';
|
|
|
+import LanguageSwitch from '@/components/LanguageSwitch';
|
|
|
+import { reportEvent } from '@/firebase';
|
|
|
+import { dialogModel } from '@/models/dialogModel';
|
|
|
+import { userModel } from '@/models/userModel';
|
|
|
+import { fetchLogin } from '@/services/login';
|
|
|
+import { createLocalTools } from '@/utils/localUtils';
|
|
|
+
|
|
|
+const { Title, Paragraph } = Typography;
|
|
|
+
|
|
|
+const Home: React.FC = () => {
|
|
|
+ const { t, i18n } = useTranslation();
|
|
|
+ const navigate = useNavigate();
|
|
|
+ const user = userModel.useModel();
|
|
|
+ const { openDialog, closeDialog } = dialogModel.useModel();
|
|
|
+ const [loading, setLoading] = useState(false);
|
|
|
+ const [loginStatus, setLoginStatus] = useState<string>('');
|
|
|
+ const [storageStatus, setStorageStatus] = useState<string>('');
|
|
|
+
|
|
|
+ useEffect(() => {
|
|
|
+ console.log('✅ home组件挂载:componentDidMount');
|
|
|
+ reportEvent('homePage_show');
|
|
|
+ return () => {
|
|
|
+ console.log('❌ home组件卸载:componentWillUnmount');
|
|
|
+ reportEvent('homePage_hide');
|
|
|
+ };
|
|
|
+ }, []);
|
|
|
+
|
|
|
+ // 创建加密存储工具实例
|
|
|
+ const encryptedLs = createLocalTools({
|
|
|
+ encryptKey: true,
|
|
|
+ encryptData: true,
|
|
|
+ });
|
|
|
+
|
|
|
+ // 创建明文存储工具实例
|
|
|
+ const plainLs = createLocalTools({
|
|
|
+ encryptKey: false,
|
|
|
+ encryptData: false,
|
|
|
+ });
|
|
|
+
|
|
|
+ // 演示加密存储 - 添加数据
|
|
|
+ const handleEncryptedStorageAdd = () => {
|
|
|
+ encryptedLs.setLocal('encryptedData', { name: '加密数据', timestamp: Date.now() });
|
|
|
+ setStorageStatus('加密数据添加成功');
|
|
|
+ };
|
|
|
+
|
|
|
+ // 演示加密存储 - 获取数据
|
|
|
+ const handleEncryptedStorageGet = () => {
|
|
|
+ const data = encryptedLs.getLocal<{ name: string; timestamp: number }>('encryptedData');
|
|
|
+ setStorageStatus(`获取加密数据: ${JSON.stringify(data)}`);
|
|
|
+ };
|
|
|
+
|
|
|
+ // 演示加密存储 - 删除数据
|
|
|
+ const handleEncryptedStorageRemove = () => {
|
|
|
+ encryptedLs.removeLocal('encryptedData');
|
|
|
+ setStorageStatus('加密数据删除成功');
|
|
|
+ };
|
|
|
+
|
|
|
+ // 演示明文存储 - 添加数据
|
|
|
+ const handlePlainStorageAdd = () => {
|
|
|
+ plainLs.setLocal('plainData', { name: '明文数据', timestamp: Date.now() });
|
|
|
+ setStorageStatus('明文数据添加成功');
|
|
|
+ };
|
|
|
+
|
|
|
+ // 演示明文存储 - 获取数据
|
|
|
+ const handlePlainStorageGet = () => {
|
|
|
+ const data = plainLs.getLocal<{ name: string; timestamp: number }>('plainData');
|
|
|
+ setStorageStatus(`获取明文数据: ${JSON.stringify(data)}`);
|
|
|
+ };
|
|
|
+
|
|
|
+ // 演示明文存储 - 删除数据
|
|
|
+ const handlePlainStorageRemove = () => {
|
|
|
+ plainLs.removeLocal('plainData');
|
|
|
+ setStorageStatus('明文数据删除成功');
|
|
|
+ };
|
|
|
+
|
|
|
+ // 演示清除所有数据
|
|
|
+ const handleStorageClear = () => {
|
|
|
+ encryptedLs.clearLocal();
|
|
|
+ plainLs.clearLocal();
|
|
|
+ setStorageStatus('所有数据清除成功');
|
|
|
+ };
|
|
|
+
|
|
|
+ // 演示网络请求
|
|
|
+ const handleLoginDemo = async () => {
|
|
|
+ setLoading(true);
|
|
|
+ try {
|
|
|
+ const result = await fetchLogin({
|
|
|
+ username: 'test',
|
|
|
+ password: '123456',
|
|
|
+ captchaId: 'test-captcha',
|
|
|
+ captchaCode: '1234',
|
|
|
+ });
|
|
|
+ setLoginStatus('登录成功');
|
|
|
+ console.log('登录结果:', result);
|
|
|
+ } catch (error) {
|
|
|
+ setLoginStatus('登录失败');
|
|
|
+ console.error('登录错误:', error);
|
|
|
+ } finally {
|
|
|
+ setLoading(false);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 演示路由跳转
|
|
|
+ const handleNavigation = (path: string) => {
|
|
|
+ navigate(path);
|
|
|
+ };
|
|
|
+
|
|
|
+ // 切换语言
|
|
|
+ const handleLanguageChange = (lang: string) => {
|
|
|
+ i18n.changeLanguage(lang);
|
|
|
+ };
|
|
|
+
|
|
|
+ // 对话框演示 - 单个按钮
|
|
|
+ const handleOpenSingleButtonDialog = () => {
|
|
|
+ openDialog({
|
|
|
+ icon: infoIcon,
|
|
|
+ title: 'Payment Failure',
|
|
|
+ content: 'Oops! Your transaction failed due to some errors, please try again.',
|
|
|
+ buttons: [
|
|
|
+ {
|
|
|
+ label: 'OK',
|
|
|
+ onClick: (_event, id) => {
|
|
|
+ closeDialog(id);
|
|
|
+ },
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ // 对话框演示 - 两个按钮
|
|
|
+ const handleOpenTwoButtonDialog = () => {
|
|
|
+ openDialog({
|
|
|
+ icon: infoIcon,
|
|
|
+ title: 'Payment verification',
|
|
|
+ content:
|
|
|
+ 'Orders are being verified, if there is no response for a long time, please contact customer service if you have any questions!',
|
|
|
+ buttons: [
|
|
|
+ {
|
|
|
+ label: 'Cancel',
|
|
|
+ variant: 'secondary',
|
|
|
+ onClick: (_event, id) => {
|
|
|
+ closeDialog(id);
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: 'Contact us',
|
|
|
+ onClick: (_event, id) => {
|
|
|
+ closeDialog(id);
|
|
|
+ },
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ // 对话框演示 - 无图标
|
|
|
+ const handleOpenNoIconDialog = () => {
|
|
|
+ openDialog({
|
|
|
+ title: 'Simple Dialog',
|
|
|
+ content: 'This is a dialog without an icon.',
|
|
|
+ buttons: [
|
|
|
+ {
|
|
|
+ label: 'Close',
|
|
|
+ onClick: (_event, id) => {
|
|
|
+ closeDialog(id);
|
|
|
+ },
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ // 对话框演示 - 自定义内容
|
|
|
+ const handleOpenCustomContentDialog = () => {
|
|
|
+ openDialog({
|
|
|
+ icon: infoIcon,
|
|
|
+ title: (
|
|
|
+ <span>
|
|
|
+ Custom <strong>Title</strong>
|
|
|
+ </span>
|
|
|
+ ),
|
|
|
+ content: (
|
|
|
+ <div>
|
|
|
+ <p>This dialog has custom ReactNode content.</p>
|
|
|
+ <ul className="list-disc list-inside mt-2">
|
|
|
+ <li>Feature 1</li>
|
|
|
+ <li>Feature 2</li>
|
|
|
+ <li>Feature 3</li>
|
|
|
+ </ul>
|
|
|
+ </div>
|
|
|
+ ),
|
|
|
+ buttons: [
|
|
|
+ {
|
|
|
+ label: 'Got it',
|
|
|
+ onClick: (_event, id) => {
|
|
|
+ closeDialog(id);
|
|
|
+ },
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ // 对话框演示 - 禁止点击遮罩层关闭
|
|
|
+ const handleOpenNonClosableDialog = () => {
|
|
|
+ openDialog({
|
|
|
+ icon: infoIcon,
|
|
|
+ title: 'Important Notice',
|
|
|
+ content: 'This dialog cannot be closed by clicking the mask. You must click the button to close it.',
|
|
|
+ maskClosable: false,
|
|
|
+ buttons: [
|
|
|
+ {
|
|
|
+ label: 'I Understand',
|
|
|
+ onClick: (_event, id) => {
|
|
|
+ closeDialog(id);
|
|
|
+ },
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ // 对话框演示 - 嵌套对话框
|
|
|
+ const handleOpenNestedDialog = () => {
|
|
|
+ openDialog({
|
|
|
+ icon: infoIcon,
|
|
|
+ title: 'First Dialog',
|
|
|
+ content: 'This is the first dialog. Click the button below to open a nested dialog.',
|
|
|
+ buttons: [
|
|
|
+ {
|
|
|
+ label: 'Open Nested Dialog',
|
|
|
+ onClick: (_event, id) => {
|
|
|
+ openDialog({
|
|
|
+ icon: infoIcon,
|
|
|
+ title: 'Nested Dialog',
|
|
|
+ content: 'This is a nested dialog opened from the first dialog. Notice how the z-index is automatically managed.',
|
|
|
+ buttons: [
|
|
|
+ {
|
|
|
+ label: 'Close Nested',
|
|
|
+ variant: 'secondary',
|
|
|
+ onClick: (_event, nestedId) => {
|
|
|
+ closeDialog(nestedId);
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: 'Close All',
|
|
|
+ onClick: (_event, nestedId) => {
|
|
|
+ closeDialog(nestedId);
|
|
|
+ closeDialog(id);
|
|
|
+ },
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ });
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: 'Close First',
|
|
|
+ variant: 'secondary',
|
|
|
+ onClick: (_event, id) => {
|
|
|
+ closeDialog(id);
|
|
|
+ },
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ return (
|
|
|
+ <div className="max-w-[1000px] mx-auto p-4">
|
|
|
+ <div className="flex justify-between items-center mb-6">
|
|
|
+ <h1 className="text-3xl font-bold text-gray-900">{t('pages.featureDemo.title')}</h1>
|
|
|
+ <LanguageSwitch />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* 多语言演示卡片 */}
|
|
|
+ <Card className="mb-6">
|
|
|
+ <Title level={3}>多语言演示</Title>
|
|
|
+ <div className="space-y-4">
|
|
|
+ <p className="text-gray-600">{t('pages.featureDemo.description')}</p>
|
|
|
+ <Space>
|
|
|
+ <Button onClick={() => handleLanguageChange('en-US')}>English</Button>
|
|
|
+ <Button onClick={() => handleLanguageChange('fa-IR')}>فارسی</Button>
|
|
|
+ </Space>
|
|
|
+ <div className="space-y-2">
|
|
|
+ <p className="text-gray-600">{t('pages.featureDemo.features.title')}</p>
|
|
|
+ <ul className="list-disc list-inside space-y-1 text-gray-600">
|
|
|
+ <li>{t('pages.featureDemo.features.antd')}</li>
|
|
|
+ <li>{t('pages.featureDemo.features.tailwind')}</li>
|
|
|
+ <li>{t('pages.featureDemo.features.i18n')}</li>
|
|
|
+ </ul>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </Card>
|
|
|
+
|
|
|
+ {/* Model 状态示例卡片 */}
|
|
|
+ <Card className="mb-6">
|
|
|
+ <Title level={3}>Model 状态示例</Title>
|
|
|
+ <div className="space-y-4">
|
|
|
+ <Paragraph className="text-gray-600">
|
|
|
+ 这个页面可以访问到用户信息,并演示 Model 状态管理:
|
|
|
+ </Paragraph>
|
|
|
+ <Paragraph className="text-gray-600">姓名:{user.name}</Paragraph>
|
|
|
+ <Paragraph className="text-gray-600">年龄:{user.age}</Paragraph>
|
|
|
+ <Space wrap className="mt-4">
|
|
|
+ <Button
|
|
|
+ onClick={() => {
|
|
|
+ const names = ['张三', '李四', '王五', '赵六', '钱七', '孙八', '周九', '吴十'];
|
|
|
+ const randomName = names[Math.floor(Math.random() * names.length)];
|
|
|
+ user.updateName(randomName);
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ 修改名字
|
|
|
+ </Button>
|
|
|
+ <Button
|
|
|
+ onClick={() => {
|
|
|
+ const randomAge = Math.floor(Math.random() * 50) + 18;
|
|
|
+ user.updateAge(randomAge);
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ 修改年龄
|
|
|
+ </Button>
|
|
|
+ <Button onClick={user.incrementAge}>年龄+1</Button>
|
|
|
+ </Space>
|
|
|
+ </div>
|
|
|
+ </Card>
|
|
|
+
|
|
|
+ {/* 功能演示卡片 */}
|
|
|
+ <Card className="mb-6">
|
|
|
+ <Title level={3}>功能演示</Title>
|
|
|
+ <div className="space-y-4">
|
|
|
+ <div>
|
|
|
+ <Title level={4}>本地存储演示</Title>
|
|
|
+ <div className="space-y-4">
|
|
|
+ <div>
|
|
|
+ <Title level={5}>加密存储</Title>
|
|
|
+ <Space wrap>
|
|
|
+ <Button onClick={handleEncryptedStorageAdd}>
|
|
|
+ 添加加密数据
|
|
|
+ </Button>
|
|
|
+ <Button onClick={handleEncryptedStorageGet}>
|
|
|
+ 获取加密数据
|
|
|
+ </Button>
|
|
|
+ <Button onClick={handleEncryptedStorageRemove}>
|
|
|
+ 删除加密数据
|
|
|
+ </Button>
|
|
|
+ </Space>
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <Title level={5}>明文存储</Title>
|
|
|
+ <Space wrap>
|
|
|
+ <Button onClick={handlePlainStorageAdd}>添加明文数据</Button>
|
|
|
+ <Button onClick={handlePlainStorageGet}>获取明文数据</Button>
|
|
|
+ <Button onClick={handlePlainStorageRemove}>删除明文数据</Button>
|
|
|
+ </Space>
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <Button danger onClick={handleStorageClear}>
|
|
|
+ 清除所有数据
|
|
|
+ </Button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ {storageStatus && <p className="mt-2 text-gray-600">{storageStatus}</p>}
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div>
|
|
|
+ <Title level={4}>网络请求演示</Title>
|
|
|
+ <Space>
|
|
|
+ <Button type="primary" onClick={handleLoginDemo} loading={loading}>
|
|
|
+ 测试登录
|
|
|
+ </Button>
|
|
|
+ {loginStatus && <span className="text-gray-600">{loginStatus}</span>}
|
|
|
+ </Space>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div>
|
|
|
+ <Title level={4}>路由跳转演示</Title>
|
|
|
+ <Space>
|
|
|
+ <Button onClick={() => handleNavigation('/route-demo')}>
|
|
|
+ 路由示例
|
|
|
+ </Button>
|
|
|
+ </Space>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </Card>
|
|
|
+
|
|
|
+ {/* 图标演示卡片 */}
|
|
|
+ <Card className="mb-6">
|
|
|
+ <Title level={3}>图标演示</Title>
|
|
|
+ <div className="space-y-6">
|
|
|
+ <div>
|
|
|
+ <Title level={4}>离线图标</Title>
|
|
|
+ <Space wrap>
|
|
|
+ <Button icon={<Icon icon={mdiHome} />}>首页</Button>
|
|
|
+ <Button icon={<Icon icon={mdiAccount} />}>用户</Button>
|
|
|
+ <Button icon={<Icon icon={mdiSettings} />}>设置</Button>
|
|
|
+ <Button icon={<Icon icon={mdiBell} />}>通知</Button>
|
|
|
+ <Button icon={<Icon icon={mdiHeart} />}>收藏</Button>
|
|
|
+ </Space>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div>
|
|
|
+ <Title level={4}>在线图标</Title>
|
|
|
+ <Space wrap>
|
|
|
+ <Button icon={<Icon icon="ri:home-line" />}>首页</Button>
|
|
|
+ <Button icon={<Icon icon="ri:user-line" />}>用户</Button>
|
|
|
+ <Button icon={<Icon icon="ri:settings-line" />}>设置</Button>
|
|
|
+ <Button icon={<Icon icon="ri:notification-line" />}>通知</Button>
|
|
|
+ <Button icon={<Icon icon="ri:heart-line" />}>收藏</Button>
|
|
|
+ </Space>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div>
|
|
|
+ <Title level={4}>自定义图标</Title>
|
|
|
+ <Space>
|
|
|
+ <Icon
|
|
|
+ icon={singleColorIcon}
|
|
|
+ className="w-[40px] h-[40px] text-red-500"
|
|
|
+ />
|
|
|
+ <Icon icon={multiColorIcon} className="w-[40px] h-[40px]" />
|
|
|
+ </Space>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div>
|
|
|
+ <Title level={4}>动画图标</Title>
|
|
|
+ <Space>
|
|
|
+ <Icon icon={mdiLoading} className="animate-spin" />
|
|
|
+ <Icon icon={mdiHeart} className="animate-bounce" />
|
|
|
+ <Icon icon={mdiBell} className="animate-pulse" />
|
|
|
+ </Space>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </Card>
|
|
|
+
|
|
|
+ {/* 样式演示卡片 */}
|
|
|
+ <Card className="mb-6">
|
|
|
+ <Title level={3}>样式演示</Title>
|
|
|
+ <div className="space-y-4">
|
|
|
+ <div>
|
|
|
+ <Title level={4}>Tailwind CSS 样式</Title>
|
|
|
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
|
+ <div className="bg-blue-500 text-white p-4 rounded-lg shadow-md">
|
|
|
+ 响应式卡片 1
|
|
|
+ </div>
|
|
|
+ <div className="bg-green-500 text-white p-4 rounded-lg shadow-md">
|
|
|
+ 响应式卡片 2
|
|
|
+ </div>
|
|
|
+ <div className="bg-purple-500 text-white p-4 rounded-lg shadow-md">
|
|
|
+ 响应式卡片 3
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div>
|
|
|
+ <Title level={4}>Ant Design 组件</Title>
|
|
|
+ <Space wrap>
|
|
|
+ <Button type="primary">主要按钮</Button>
|
|
|
+ <Button>默认按钮</Button>
|
|
|
+ <Button type="dashed">虚线按钮</Button>
|
|
|
+ <Button type="link">链接按钮</Button>
|
|
|
+ </Space>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </Card>
|
|
|
+
|
|
|
+ {/* 对话框演示卡片 */}
|
|
|
+ <Card className="mb-6">
|
|
|
+ <Title level={3}>对话框演示</Title>
|
|
|
+ <div className="space-y-4">
|
|
|
+ <div>
|
|
|
+ <Title level={4}>Dialog 组件示例</Title>
|
|
|
+ <Space wrap>
|
|
|
+ <Button type="primary" onClick={handleOpenSingleButtonDialog}>
|
|
|
+ 单个按钮对话框
|
|
|
+ </Button>
|
|
|
+ <Button type="primary" onClick={handleOpenTwoButtonDialog}>
|
|
|
+ 两个按钮对话框
|
|
|
+ </Button>
|
|
|
+ <Button onClick={handleOpenNoIconDialog}>无图标对话框</Button>
|
|
|
+ <Button onClick={handleOpenCustomContentDialog}>
|
|
|
+ 自定义内容对话框
|
|
|
+ </Button>
|
|
|
+ <Button onClick={handleOpenNonClosableDialog}>
|
|
|
+ 禁止遮罩关闭对话框
|
|
|
+ </Button>
|
|
|
+ <Button onClick={handleOpenNestedDialog}>嵌套对话框</Button>
|
|
|
+ </Space>
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <Title level={5}>使用说明</Title>
|
|
|
+ <ul className="list-disc list-inside space-y-1 text-gray-600">
|
|
|
+ <li>通过 dialogModel.openDialog() 打开对话框</li>
|
|
|
+ <li>openDialog 返回对话框 id,可用于后续关闭</li>
|
|
|
+ <li>按钮的 onClick 接收 (event, dialogId) 两个参数</li>
|
|
|
+ <li>默认情况下,点击遮罩层可以关闭对话框</li>
|
|
|
+ <li>设置 maskClosable: false 可禁止点击遮罩层关闭</li>
|
|
|
+ <li>支持嵌套对话框,z-index 自动管理</li>
|
|
|
+ <li>支持图标、标题、内容的自定义</li>
|
|
|
+ </ul>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </Card>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+};
|
|
|
+
|
|
|
+export default Home;
|