index.tsx 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502
  1. import React, { useEffect, useState } from 'react';
  2. import { Icon } from '@iconify/react';
  3. import mdiAccount from '@iconify-icons/mdi/account';
  4. import mdiBell from '@iconify-icons/mdi/bell';
  5. import mdiHeart from '@iconify-icons/mdi/heart';
  6. import mdiHome from '@iconify-icons/mdi/home';
  7. import mdiLoading from '@iconify-icons/mdi/loading';
  8. import mdiSettings from '@iconify-icons/mdi/settings';
  9. import Button from 'antd/es/button';
  10. import Card from 'antd/es/card';
  11. import Space from 'antd/es/space';
  12. import Typography from 'antd/es/typography';
  13. import { useTranslation } from 'react-i18next';
  14. import { useNavigate } from 'react-router-dom';
  15. import multiColorIcon from '@/assets/iconify/multi-color/logo.svg';
  16. import singleColorIcon from '@/assets/iconify/single-color/home.svg';
  17. import infoIcon from '@/assets/iconify/single-color/info.svg';
  18. import LanguageSwitch from '@/components/LanguageSwitch';
  19. import { reportEvent } from '@/firebase';
  20. import { dialogModel } from '@/models/dialogModel';
  21. import { userModel } from '@/models/userModel';
  22. import { fetchLogin } from '@/services/login';
  23. import { createLocalTools } from '@/utils/localUtils';
  24. const { Title } = Typography;
  25. const Home: React.FC = () => {
  26. const { t, i18n } = useTranslation();
  27. const navigate = useNavigate();
  28. const user = userModel.useModel();
  29. const { openDialog, closeDialog } = dialogModel.useModel();
  30. const [loading, setLoading] = useState(false);
  31. const [loginStatus, setLoginStatus] = useState<string>('');
  32. const [storageStatus, setStorageStatus] = useState<string>('');
  33. useEffect(() => {
  34. console.log('✅ home组件挂载:componentDidMount');
  35. reportEvent('homePage_show');
  36. return () => {
  37. console.log('❌ home组件卸载:componentWillUnmount');
  38. reportEvent('homePage_hide');
  39. };
  40. }, []);
  41. // 创建加密存储工具实例
  42. const encryptedLs = createLocalTools({
  43. encryptKey: true,
  44. encryptData: true,
  45. });
  46. // 创建明文存储工具实例
  47. const plainLs = createLocalTools({
  48. encryptKey: false,
  49. encryptData: false,
  50. });
  51. // 演示加密存储 - 添加数据
  52. const handleEncryptedStorageAdd = () => {
  53. encryptedLs.setLocal('encryptedData', { name: '加密数据', timestamp: Date.now() });
  54. setStorageStatus('加密数据添加成功');
  55. };
  56. // 演示加密存储 - 获取数据
  57. const handleEncryptedStorageGet = () => {
  58. const data = encryptedLs.getLocal<{ name: string; timestamp: number }>('encryptedData');
  59. setStorageStatus(`获取加密数据: ${JSON.stringify(data)}`);
  60. };
  61. // 演示加密存储 - 删除数据
  62. const handleEncryptedStorageRemove = () => {
  63. encryptedLs.removeLocal('encryptedData');
  64. setStorageStatus('加密数据删除成功');
  65. };
  66. // 演示明文存储 - 添加数据
  67. const handlePlainStorageAdd = () => {
  68. plainLs.setLocal('plainData', { name: '明文数据', timestamp: Date.now() });
  69. setStorageStatus('明文数据添加成功');
  70. };
  71. // 演示明文存储 - 获取数据
  72. const handlePlainStorageGet = () => {
  73. const data = plainLs.getLocal<{ name: string; timestamp: number }>('plainData');
  74. setStorageStatus(`获取明文数据: ${JSON.stringify(data)}`);
  75. };
  76. // 演示明文存储 - 删除数据
  77. const handlePlainStorageRemove = () => {
  78. plainLs.removeLocal('plainData');
  79. setStorageStatus('明文数据删除成功');
  80. };
  81. // 演示清除所有数据
  82. const handleStorageClear = () => {
  83. encryptedLs.clearLocal();
  84. plainLs.clearLocal();
  85. setStorageStatus('所有数据清除成功');
  86. };
  87. // 演示网络请求
  88. const handleLoginDemo = async () => {
  89. setLoading(true);
  90. try {
  91. const result = await fetchLogin({
  92. username: 'test',
  93. password: '123456',
  94. captchaId: 'test-captcha',
  95. captchaCode: '1234',
  96. });
  97. setLoginStatus('登录成功');
  98. console.log('登录结果:', result);
  99. } catch (error) {
  100. setLoginStatus('登录失败');
  101. console.error('登录错误:', error);
  102. } finally {
  103. setLoading(false);
  104. }
  105. };
  106. // 演示路由跳转
  107. const handleNavigation = (path: string) => {
  108. navigate(path);
  109. };
  110. // 切换语言
  111. const handleLanguageChange = (lang: string) => {
  112. i18n.changeLanguage(lang);
  113. };
  114. // 对话框演示 - 单个按钮
  115. const handleOpenSingleButtonDialog = () => {
  116. openDialog({
  117. icon: infoIcon,
  118. title: 'Payment Failure',
  119. content: 'Oops! Your transaction failed due to some errors, please try again.',
  120. buttons: [
  121. {
  122. label: 'OK',
  123. onClick: (_event, id) => {
  124. closeDialog(id);
  125. },
  126. },
  127. ],
  128. });
  129. };
  130. // 对话框演示 - 两个按钮
  131. const handleOpenTwoButtonDialog = () => {
  132. openDialog({
  133. icon: infoIcon,
  134. title: 'Payment verification',
  135. content:
  136. 'Orders are being verified, if there is no response for a long time, please contact customer service if you have any questions!',
  137. buttons: [
  138. {
  139. label: 'Cancel',
  140. variant: 'secondary',
  141. onClick: (_event, id) => {
  142. closeDialog(id);
  143. },
  144. },
  145. {
  146. label: 'Contact us',
  147. onClick: (_event, id) => {
  148. closeDialog(id);
  149. },
  150. },
  151. ],
  152. });
  153. };
  154. // 对话框演示 - 无图标
  155. const handleOpenNoIconDialog = () => {
  156. openDialog({
  157. title: 'Simple Dialog',
  158. content: 'This is a dialog without an icon.',
  159. buttons: [
  160. {
  161. label: 'Close',
  162. onClick: (_event, id) => {
  163. closeDialog(id);
  164. },
  165. },
  166. ],
  167. });
  168. };
  169. // 对话框演示 - 自定义内容
  170. const handleOpenCustomContentDialog = () => {
  171. openDialog({
  172. icon: infoIcon,
  173. title: (
  174. <span>
  175. Custom <strong>Title</strong>
  176. </span>
  177. ),
  178. content: (
  179. <div>
  180. <p>This dialog has custom ReactNode content.</p>
  181. <ul className="list-disc list-inside mt-2">
  182. <li>Feature 1</li>
  183. <li>Feature 2</li>
  184. <li>Feature 3</li>
  185. </ul>
  186. </div>
  187. ),
  188. buttons: [
  189. {
  190. label: 'Got it',
  191. onClick: (_event, id) => {
  192. closeDialog(id);
  193. },
  194. },
  195. ],
  196. });
  197. };
  198. // 对话框演示 - 禁止点击遮罩层关闭
  199. const handleOpenNonClosableDialog = () => {
  200. openDialog({
  201. icon: infoIcon,
  202. title: 'Important Notice',
  203. content: 'This dialog cannot be closed by clicking the mask. You must click the button to close it.',
  204. maskClosable: false,
  205. buttons: [
  206. {
  207. label: 'I Understand',
  208. onClick: (_event, id) => {
  209. closeDialog(id);
  210. },
  211. },
  212. ],
  213. });
  214. };
  215. // 对话框演示 - 嵌套对话框
  216. const handleOpenNestedDialog = () => {
  217. openDialog({
  218. icon: infoIcon,
  219. title: 'First Dialog',
  220. content: 'This is the first dialog. Click the button below to open a nested dialog.',
  221. buttons: [
  222. {
  223. label: 'Open Nested Dialog',
  224. onClick: (_event, id) => {
  225. openDialog({
  226. icon: infoIcon,
  227. title: 'Nested Dialog',
  228. content: 'This is a nested dialog opened from the first dialog. Notice how the z-index is automatically managed.',
  229. buttons: [
  230. {
  231. label: 'Close Nested',
  232. variant: 'secondary',
  233. onClick: (_event, nestedId) => {
  234. closeDialog(nestedId);
  235. },
  236. },
  237. {
  238. label: 'Close All',
  239. onClick: (_event, nestedId) => {
  240. closeDialog(nestedId);
  241. closeDialog(id);
  242. },
  243. },
  244. ],
  245. });
  246. },
  247. },
  248. {
  249. label: 'Close First',
  250. variant: 'secondary',
  251. onClick: (_event, id) => {
  252. closeDialog(id);
  253. },
  254. },
  255. ],
  256. });
  257. };
  258. return (
  259. <div className="max-w-[1000px] mx-auto p-4">
  260. <div className="flex justify-between items-center mb-6">
  261. <h1 className="text-3xl font-bold text-gray-900">{t('pages.home.title')}</h1>
  262. <LanguageSwitch />
  263. </div>
  264. {/* 多语言演示卡片 */}
  265. <Card className="mb-6">
  266. <Title level={3}>多语言演示</Title>
  267. <div className="space-y-4">
  268. <p className="text-gray-600">{t('pages.home.description')}</p>
  269. <Space>
  270. <Button onClick={() => handleLanguageChange('en-US')}>English</Button>
  271. <Button onClick={() => handleLanguageChange('fa-IR')}>فارسی</Button>
  272. </Space>
  273. <div className="space-y-2">
  274. <p className="text-gray-600">{t('pages.home.features.title')}</p>
  275. <ul className="list-disc list-inside space-y-1 text-gray-600">
  276. <li>{t('pages.home.features.antd')}</li>
  277. <li>{t('pages.home.features.tailwind')}</li>
  278. <li>{t('pages.home.features.i18n')}</li>
  279. </ul>
  280. </div>
  281. </div>
  282. </Card>
  283. {/* 用户信息卡片 */}
  284. <Card className="mb-6">
  285. <Title level={3}>用户信息</Title>
  286. <div className="space-y-4">
  287. <p className="text-gray-600">姓名:{user.name}</p>
  288. <p className="text-gray-600">年龄:{user.age}</p>
  289. <Space wrap>
  290. <Button onClick={() => user.updateName('张三')}>修改名字</Button>
  291. <Button onClick={() => user.updateAge(25)}>修改年龄</Button>
  292. <Button onClick={user.incrementAge}>年龄+1</Button>
  293. </Space>
  294. </div>
  295. </Card>
  296. {/* 功能演示卡片 */}
  297. <Card className="mb-6">
  298. <Title level={3}>功能演示</Title>
  299. <div className="space-y-4">
  300. <div>
  301. <Title level={4}>本地存储演示</Title>
  302. <div className="space-y-4">
  303. <div>
  304. <Title level={5}>加密存储</Title>
  305. <Space wrap>
  306. <Button onClick={handleEncryptedStorageAdd}>
  307. 添加加密数据
  308. </Button>
  309. <Button onClick={handleEncryptedStorageGet}>
  310. 获取加密数据
  311. </Button>
  312. <Button onClick={handleEncryptedStorageRemove}>
  313. 删除加密数据
  314. </Button>
  315. </Space>
  316. </div>
  317. <div>
  318. <Title level={5}>明文存储</Title>
  319. <Space wrap>
  320. <Button onClick={handlePlainStorageAdd}>添加明文数据</Button>
  321. <Button onClick={handlePlainStorageGet}>获取明文数据</Button>
  322. <Button onClick={handlePlainStorageRemove}>删除明文数据</Button>
  323. </Space>
  324. </div>
  325. <div>
  326. <Button danger onClick={handleStorageClear}>
  327. 清除所有数据
  328. </Button>
  329. </div>
  330. </div>
  331. {storageStatus && <p className="mt-2 text-gray-600">{storageStatus}</p>}
  332. </div>
  333. <div>
  334. <Title level={4}>网络请求演示</Title>
  335. <Space>
  336. <Button type="primary" onClick={handleLoginDemo} loading={loading}>
  337. 测试登录
  338. </Button>
  339. {loginStatus && <span className="text-gray-600">{loginStatus}</span>}
  340. </Space>
  341. </div>
  342. <div>
  343. <Title level={4}>路由跳转演示</Title>
  344. <Space>
  345. <Button onClick={() => handleNavigation('/route-demo')}>
  346. 路由示例
  347. </Button>
  348. </Space>
  349. </div>
  350. </div>
  351. </Card>
  352. {/* 图标演示卡片 */}
  353. <Card className="mb-6">
  354. <Title level={3}>图标演示</Title>
  355. <div className="space-y-6">
  356. <div>
  357. <Title level={4}>离线图标</Title>
  358. <Space wrap>
  359. <Button icon={<Icon icon={mdiHome} />}>首页</Button>
  360. <Button icon={<Icon icon={mdiAccount} />}>用户</Button>
  361. <Button icon={<Icon icon={mdiSettings} />}>设置</Button>
  362. <Button icon={<Icon icon={mdiBell} />}>通知</Button>
  363. <Button icon={<Icon icon={mdiHeart} />}>收藏</Button>
  364. </Space>
  365. </div>
  366. <div>
  367. <Title level={4}>在线图标</Title>
  368. <Space wrap>
  369. <Button icon={<Icon icon="ri:home-line" />}>首页</Button>
  370. <Button icon={<Icon icon="ri:user-line" />}>用户</Button>
  371. <Button icon={<Icon icon="ri:settings-line" />}>设置</Button>
  372. <Button icon={<Icon icon="ri:notification-line" />}>通知</Button>
  373. <Button icon={<Icon icon="ri:heart-line" />}>收藏</Button>
  374. </Space>
  375. </div>
  376. <div>
  377. <Title level={4}>自定义图标</Title>
  378. <Space>
  379. <Icon
  380. icon={singleColorIcon}
  381. className="w-[40px] h-[40px] text-red-500"
  382. />
  383. <Icon icon={multiColorIcon} className="w-[40px] h-[40px]" />
  384. </Space>
  385. </div>
  386. <div>
  387. <Title level={4}>动画图标</Title>
  388. <Space>
  389. <Icon icon={mdiLoading} className="animate-spin" />
  390. <Icon icon={mdiHeart} className="animate-bounce" />
  391. <Icon icon={mdiBell} className="animate-pulse" />
  392. </Space>
  393. </div>
  394. </div>
  395. </Card>
  396. {/* 样式演示卡片 */}
  397. <Card className="mb-6">
  398. <Title level={3}>样式演示</Title>
  399. <div className="space-y-4">
  400. <div>
  401. <Title level={4}>Tailwind CSS 样式</Title>
  402. <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
  403. <div className="bg-blue-500 text-white p-4 rounded-lg shadow-md">
  404. 响应式卡片 1
  405. </div>
  406. <div className="bg-green-500 text-white p-4 rounded-lg shadow-md">
  407. 响应式卡片 2
  408. </div>
  409. <div className="bg-purple-500 text-white p-4 rounded-lg shadow-md">
  410. 响应式卡片 3
  411. </div>
  412. </div>
  413. </div>
  414. <div>
  415. <Title level={4}>Ant Design 组件</Title>
  416. <Space wrap>
  417. <Button type="primary">主要按钮</Button>
  418. <Button>默认按钮</Button>
  419. <Button type="dashed">虚线按钮</Button>
  420. <Button type="link">链接按钮</Button>
  421. </Space>
  422. </div>
  423. </div>
  424. </Card>
  425. {/* 对话框演示卡片 */}
  426. <Card className="mb-6">
  427. <Title level={3}>对话框演示</Title>
  428. <div className="space-y-4">
  429. <div>
  430. <Title level={4}>Dialog 组件示例</Title>
  431. <Space wrap>
  432. <Button type="primary" onClick={handleOpenSingleButtonDialog}>
  433. 单个按钮对话框
  434. </Button>
  435. <Button type="primary" onClick={handleOpenTwoButtonDialog}>
  436. 两个按钮对话框
  437. </Button>
  438. <Button onClick={handleOpenNoIconDialog}>无图标对话框</Button>
  439. <Button onClick={handleOpenCustomContentDialog}>
  440. 自定义内容对话框
  441. </Button>
  442. <Button onClick={handleOpenNonClosableDialog}>
  443. 禁止遮罩关闭对话框
  444. </Button>
  445. <Button onClick={handleOpenNestedDialog}>嵌套对话框</Button>
  446. </Space>
  447. </div>
  448. <div>
  449. <Title level={5}>使用说明</Title>
  450. <ul className="list-disc list-inside space-y-1 text-gray-600">
  451. <li>通过 dialogModel.openDialog() 打开对话框</li>
  452. <li>openDialog 返回对话框 id,可用于后续关闭</li>
  453. <li>按钮的 onClick 接收 (event, dialogId) 两个参数</li>
  454. <li>默认情况下,点击遮罩层可以关闭对话框</li>
  455. <li>设置 maskClosable: false 可禁止点击遮罩层关闭</li>
  456. <li>支持嵌套对话框,z-index 自动管理</li>
  457. <li>支持图标、标题、内容的自定义</li>
  458. </ul>
  459. </div>
  460. </div>
  461. </Card>
  462. </div>
  463. );
  464. };
  465. export default Home;