diff --git a/apps/miniprogram/package.json b/apps/miniprogram/package.json index 6f044cf..89dfe55 100644 --- a/apps/miniprogram/package.json +++ b/apps/miniprogram/package.json @@ -14,9 +14,6 @@ "ios >= 8" ], "dependencies": { - "@babel/preset-env": "^7.29.2", - "@babel/preset-react": "^7.28.5", - "@babel/preset-typescript": "^7.28.5", "@tarojs/components": "4.2.0", "@tarojs/helper": "4.2.0", "@tarojs/plugin-framework-react": "4.2.0", @@ -25,20 +22,18 @@ "@tarojs/runtime": "4.2.0", "@tarojs/shared": "4.2.0", "@tarojs/taro": "4.2.0", - "babel-preset-taro": "^4.2.0", - "crypto-js": "^4.2.0", - "echarts": "^6.0.0", "react": "^18.3.0", - "react-dom": "^18.3.0", - "zod": "^4.3.6", "zustand": "^5.0.0" }, "devDependencies": { + "@babel/preset-env": "^7.29.2", + "@babel/preset-react": "^7.28.5", + "@babel/preset-typescript": "^7.28.5", "@babel/runtime": "^7.27.0", "@tarojs/cli": "4.2.0", "@tarojs/webpack5-runner": "4.2.0", - "@types/crypto-js": "^4.2.2", "@types/react": "^18.3.0", + "babel-preset-taro": "^4.2.0", "miniprogram-automator": "^0.12.1", "sass": "^1.87.0", "typescript": "^5.8.0", diff --git a/apps/miniprogram/src/hooks/useAuthRequired.ts b/apps/miniprogram/src/hooks/useAuthRequired.ts new file mode 100644 index 0000000..81dd1bf --- /dev/null +++ b/apps/miniprogram/src/hooks/useAuthRequired.ts @@ -0,0 +1,15 @@ +import { useAuthStore } from '@/stores/auth'; + +export function useAuthRequired() { + const user = useAuthStore((s) => s.user); + const currentPatient = useAuthStore((s) => s.currentPatient); + const loadPatients = useAuthStore((s) => s.loadPatients); + const isMedicalStaff = useAuthStore((s) => s.isMedicalStaff); + + return { + isLoggedIn: !!user, + hasPatient: !!currentPatient, + isMedicalStaff: isMedicalStaff(), + loadPatients, + }; +} diff --git a/apps/miniprogram/src/hooks/usePageRefresh.ts b/apps/miniprogram/src/hooks/usePageRefresh.ts new file mode 100644 index 0000000..69da321 --- /dev/null +++ b/apps/miniprogram/src/hooks/usePageRefresh.ts @@ -0,0 +1,23 @@ +import { useCallback } from 'react'; +import Taro, { usePullDownRefresh } from '@tarojs/taro'; + +export function usePageRefresh(onRefresh: () => Promise) { + usePullDownRefresh(async () => { + try { + await onRefresh(); + } finally { + Taro.stopPullDownRefresh(); + } + }); + + const manualRefresh = useCallback(async () => { + Taro.startPullDownRefresh(); + try { + await onRefresh(); + } finally { + Taro.stopPullDownRefresh(); + } + }, [onRefresh]); + + return { manualRefresh }; +} diff --git a/apps/miniprogram/src/hooks/usePagination.ts b/apps/miniprogram/src/hooks/usePagination.ts new file mode 100644 index 0000000..008221d --- /dev/null +++ b/apps/miniprogram/src/hooks/usePagination.ts @@ -0,0 +1,65 @@ +import { useState, useCallback, useRef } from 'react'; + +interface PaginationResult { + list: T[]; + setList: React.Dispatch>; + loading: boolean; + hasMore: boolean; + total: number; + loadMore: () => Promise; + refresh: () => Promise; +} + +export function usePagination( + fetcher: (page: number, pageSize: number) => Promise<{ data: T[]; total: number }>, + pageSize = 10, +): PaginationResult { + const [list, setList] = useState([]); + const [loading, setLoading] = useState(false); + const [hasMore, setHasMore] = useState(true); + const [total, setTotal] = useState(0); + const pageRef = useRef(1); + const loadingRef = useRef(false); + + const loadMore = useCallback(async () => { + if (loadingRef.current || !hasMore) return; + loadingRef.current = true; + setLoading(true); + try { + const res = await fetcher(pageRef.current, pageSize); + const items = res.data || []; + setList((prev) => [...prev, ...items]); + setTotal(res.total); + setHasMore(items.length >= pageSize); + pageRef.current += 1; + } catch { + // 错误由调用方处理 + } finally { + loadingRef.current = false; + setLoading(false); + } + }, [fetcher, pageSize, hasMore]); + + const refresh = useCallback(async () => { + if (loadingRef.current) return; + loadingRef.current = true; + setLoading(true); + pageRef.current = 1; + setHasMore(true); + try { + const res = await fetcher(1, pageSize); + const items = res.data || []; + setList(items); + setTotal(res.total); + setHasMore(items.length >= pageSize); + pageRef.current = 2; + } catch { + // 错误由调用方处理 + } finally { + loadingRef.current = false; + setLoading(false); + } + }, [fetcher, pageSize]); + + return { list, setList, loading, hasMore, total, loadMore, refresh }; +} diff --git a/apps/miniprogram/src/pages/ai-report/detail/index.tsx b/apps/miniprogram/src/pages/ai-report/detail/index.tsx index db899e4..bf6e248 100644 --- a/apps/miniprogram/src/pages/ai-report/detail/index.tsx +++ b/apps/miniprogram/src/pages/ai-report/detail/index.tsx @@ -3,6 +3,7 @@ import { View, Text, RichText } from '@tarojs/components'; import Taro, { useRouter } from '@tarojs/taro'; import { getAiAnalysisDetail, type AiAnalysisItem } from '@/services/ai-analysis'; import Loading from '@/components/Loading'; +import { sanitizeHtml } from '@/utils/sanitize-html'; import { useElderClass } from '../../../hooks/useElderClass'; import './index.scss'; @@ -13,29 +14,10 @@ const TYPE_LABELS: Record = { report_summary_generation: '报告摘要', }; -/** 移除危险的 HTML 标签和事件属性,防止 XSS */ -function sanitizeHtml(html: string): string { - return html - // 移除