From d080a2a48d6c6f5753c864d02e8515833fbaed47 Mon Sep 17 00:00:00 2001 From: oneao Date: Wed, 8 Jul 2026 22:16:10 +0800 Subject: [PATCH] 20260708221610 --- code/app/app-rn/.env.development | 2 +- .../src/app/(main)/(tabs)/chat/index.tsx | 232 ++++++++++++------ .../src/app/(main)/(tabs)/home/index.tsx | 12 +- .../app-rn/src/app/(main)/chat/history.tsx | 2 +- .../app/app-rn/src/app/(main)/tools/index.tsx | 9 +- .../src/app/clothes/(tabs)/home/index.tsx | 2 +- .../src/app/clothes/(tabs)/outfit/index.tsx | 2 +- .../src/app/clothes/detail/[id]/index.tsx | 2 +- .../app/app-rn/src/app/clothes/edit/index.tsx | 2 +- .../app/clothes/outfit/detail/[id]/index.tsx | 2 +- .../src/app/clothes/outfit/edit/index.tsx | 2 +- .../src/app/meal/(tabs)/dishes/index.tsx | 121 ++++++++- .../app-rn/src/app/meal/dish/edit/index.tsx | 4 +- .../src/app/meal/plan/detail/[id]/index.tsx | 2 +- .../app-rn/src/app/meal/plan/edit/index.tsx | 2 +- code/app/app-rn/src/configs/pages.ts | 21 +- .../app-rn/src/hooks/useModuleChatNotifier.ts | 2 +- .../src/static/images/subapps/caidan.png | Bin 0 -> 5380 bytes .../src/static/images/subapps/caidan.webp | Bin 0 -> 3416 bytes .../src/static/images/subapps/jizhang.png | Bin 0 -> 9319 bytes .../src/static/images/subapps/jizhang.webp | Bin 0 -> 7504 bytes .../src/static/images/subapps/yifudapei.png | Bin 0 -> 10031 bytes .../src/static/images/subapps/yifudapei.webp | Bin 0 -> 7386 bytes code/app/app-rn/src/types/chat.ts | 4 +- code/app/app-rn/src/types/global.d.ts | 18 ++ code/app/app-rn/tsconfig.json | 1 + 26 files changed, 329 insertions(+), 115 deletions(-) create mode 100644 code/app/app-rn/src/static/images/subapps/caidan.png create mode 100644 code/app/app-rn/src/static/images/subapps/caidan.webp create mode 100644 code/app/app-rn/src/static/images/subapps/jizhang.png create mode 100644 code/app/app-rn/src/static/images/subapps/jizhang.webp create mode 100644 code/app/app-rn/src/static/images/subapps/yifudapei.png create mode 100644 code/app/app-rn/src/static/images/subapps/yifudapei.webp diff --git a/code/app/app-rn/.env.development b/code/app/app-rn/.env.development index 9d252886..d2bcf72b 100644 --- a/code/app/app-rn/.env.development +++ b/code/app/app-rn/.env.development @@ -1,3 +1,3 @@ -EXPO_PUBLIC_API_URL=http://192.168.5.72:8087 +EXPO_PUBLIC_API_URL=http://192.168.5.80:8087 EXPO_PUBLIC_G3_URL=http://118.89.70.199:9001 EXPO_PUBLIC_G3_BUCKET=test \ No newline at end of file diff --git a/code/app/app-rn/src/app/(main)/(tabs)/chat/index.tsx b/code/app/app-rn/src/app/(main)/(tabs)/chat/index.tsx index 49dd0e1c..75f80a1a 100644 --- a/code/app/app-rn/src/app/(main)/(tabs)/chat/index.tsx +++ b/code/app/app-rn/src/app/(main)/(tabs)/chat/index.tsx @@ -1,5 +1,8 @@ import { useCallback, useEffect, useRef, useState, memo, useMemo } from "react"; -import { Image, Text, TextInput, View } from "react-native"; +import JizhangIcon from "@/static/images/subapps/jizhang.webp"; +import CaidanIcon from "@/static/images/subapps/caidan.webp"; +import YifudapeiIcon from "@/static/images/subapps/yifudapei.webp"; +import { Image, ImageSourcePropType, Text, TextInput, TouchableOpacity, View } from "react-native"; import { KeyboardAvoidingView } from "react-native-keyboard-controller"; import { useRouter } from "expo-router"; import { Menu, PressableFeedback, Spinner } from "heroui-native"; @@ -28,6 +31,58 @@ function formatTime(ts: string) { return `${d.getHours().toString().padStart(2, "0")}:${d.getMinutes().toString().padStart(2, "0")}`; } +// ===================== 模块卡片配置 ===================== + +const SUBAPP_ICONS: Record = { + finance: JizhangIcon, + meal: CaidanIcon, + clothes: YifudapeiIcon, +}; + +const SOURCE_CARD_CONFIG: Record string }> = { + b_finance: { label: "记账", icon: SUBAPP_ICONS.finance, route: (id) => `/finance/detail/${id}` }, + b_todo: { label: "待办", route: (id) => `/todo/detail/${id}` }, + b_meal_dish: { label: "家庭菜单", icon: SUBAPP_ICONS.meal, route: (id) => `/meal/dish/detail/${id}` }, + b_meal_plan: { label: "家庭菜单", icon: SUBAPP_ICONS.meal, route: (id) => `/meal/plan/detail/${id}` }, + b_clothing_outfit: { label: "衣服穿搭", icon: SUBAPP_ICONS.clothes, route: (id) => `/clothes/outfit/detail/${id}` }, + b_clothing_clothes: { label: "衣服穿搭", icon: SUBAPP_ICONS.clothes, route: (id) => `/clothes/detail/${id}` }, +}; + +/** 从 content 中解析模块名和正文,格式:『【模块】正文』 */ +function parseModuleContent(content: string): { module: string; body: string } { + const m = content.match(/^【(.+?)】(.*)$/); + return m ? { module: m[1], body: m[2] } : { module: "", body: content }; +} + +/** 根据 source_table 从 source_data 中提取关键信息 */ +function formatSourceDetail(sourceTable: string, sourceData: string | null): string | null { + if (!sourceData) return null; + try { + const d = JSON.parse(sourceData); + switch (sourceTable) { + case "b_finance": { + const parts: string[] = []; + if (d.amount != null) parts.push(`¥${d.amount}`); + if (d.remark) parts.push(d.remark); + return parts.join(" · ") || null; + } + case "b_todo": + if (d.description) return String(d.description); + return null; + case "b_meal_dish": { + const parts: string[] = []; + if (d.cooking_time_minutes) parts.push(`约${d.cooking_time_minutes}分钟`); + if (d.difficulty) parts.push(`${"★".repeat(d.difficulty)}`); + return parts.join(" · ") || null; + } + default: + return null; + } + } catch { + return null; + } +} + // ===================== 在线栏 ===================== function ChatOnlineBar({ users, onSearch }: { users: OnlineUser[]; onSearch: () => void }) { @@ -100,6 +155,18 @@ const ChatMessageItem = memo(function ChatMessageItem({ const canRecall = isSelf && within2Min; const canShowActions = canRecall || isAdmin; + // 是否为模块卡片消息(Forward 或带 source 的 System) + const isCardMsg = item.msg_type === ChatMsgType.Forward || (item.msg_type === ChatMsgType.System && !!item.source_table); + const cardConfig = isCardMsg && item.source_table ? SOURCE_CARD_CONFIG[item.source_table] : null; + const { module: moduleName, body: moduleBody } = parseModuleContent(item.content); + const sourceDetail = isCardMsg && item.source_table ? formatSourceDetail(item.source_table, item.source_data) : null; + + const handleCardPress = useCallback(() => { + if (cardConfig && item.source_id) { + router.push(cardConfig.route(item.source_id) as any); + } + }, [cardConfig, item.source_id, router]); + if (item.recalled === 1) { return ( @@ -108,7 +175,7 @@ const ChatMessageItem = memo(function ChatMessageItem({ ); } - if (item.msg_type === ChatMsgType.System) { + if (item.msg_type === ChatMsgType.System && !item.source_table) { return ( @@ -229,40 +296,46 @@ const ChatMessageItem = memo(function ChatMessageItem({ )} - {item.msg_type === ChatMsgType.Forward && item.source_data && ( - - - 来自 {item.source_table} - - {(() => { - try { - const d = JSON.parse(item.source_data); - return Object.entries(d) - .filter( - ([k]) => - ![ - "id", - "create_time", - "update_time", - "create_by", - "update_by", - "family_id", - ].includes(k), - ) - .map(([k, v]) => `${k}: ${v}`) - .join(" "); - } catch { - return item.source_data; - } - })()} - - - {item.content ? ( - - {item.content} + {isCardMsg && ( + + + {/* 模块标识:图标 + 名称 */} + + {cardConfig?.icon ? ( + + ) : ( + + )} + + {cardConfig?.label ?? moduleName ?? "动态"} + - ) : null} - + + {/* 操作描述 */} + + {moduleBody || item.content} + + + {/* 来源补充信息 */} + {sourceDetail && ( + + {sourceDetail} + + )} + + )} @@ -547,7 +620,7 @@ export default function ChatPage() { } // Retry a failed message - function handleRetry(p: PendingMsg) { + const handleRetry = useCallback((p: PendingMsg) => { setPending((prev) => prev.map((x) => (x.localId === p.localId ? { ...x, status: "sending" } : x)), ); @@ -559,7 +632,50 @@ export default function ChatPage() { ); toast.error("发送失败", { id: p.localId }); }, 10_000); - } + }, [ws]); + + // memo 稳定引用,避免 inverted FlatList 反复 renderItem + const keyExtractor = useCallback((item: any) => { + const p = item._pending; + return p ? p.localId : String(item.id); + }, []); + + const renderItem = useCallback(({ item }: { item: any }) => { + const pending = item._pending as PendingMsg | undefined; + if (pending) { + return ( + + {pending.status === "sending" ? ( + + + + ) : ( + handleRetry(pending)}> + + + + + + )} + + + {pending.content} + + + + ); + } + return ( + + ); + }, [userId, members, adminRole, ws.recall, ws.deleteMessage, handleRetry]); if (loading) { return ( @@ -612,10 +728,7 @@ export default function ChatPage() { } data={invertedMessages} inverted - keyExtractor={(item) => { - const p = (item as any)._pending; - return p ? p.localId : String(item.id); - }} + keyExtractor={keyExtractor} contentContainerStyle={{ paddingTop: 40 }} onEndReached={() => { if (hasMore && !loadingMore) handleLoadMore(); @@ -637,42 +750,7 @@ export default function ChatPage() { 暂无消息,开始聊天吧 } - renderItem={({ item }: { item: any }) => { - const pending = item._pending as PendingMsg | undefined; - if (pending) { - return ( - - {pending.status === "sending" ? ( - - - - ) : ( - handleRetry(pending)}> - - - - - - )} - - - {pending.content} - - - - ); - } - return ( - - ); - }} + renderItem={renderItem} /> diff --git a/code/app/app-rn/src/app/(main)/(tabs)/home/index.tsx b/code/app/app-rn/src/app/(main)/(tabs)/home/index.tsx index 9393be24..f6d87ebd 100644 --- a/code/app/app-rn/src/app/(main)/(tabs)/home/index.tsx +++ b/code/app/app-rn/src/app/(main)/(tabs)/home/index.tsx @@ -1,5 +1,5 @@ import { useEffect, useState } from "react"; -import { View, Text } from "react-native"; +import { Image, View, Text } from "react-native"; import { PressableFeedback } from "heroui-native"; import { useFamilyStore, useFavoriteStore } from "@/store"; import { loadDataApi } from "@/request/api"; @@ -34,17 +34,17 @@ function SectionLabel({ // ── 组件:宫格项 ────────────────────────────────── function GridItem({ - icon: Icon, + icon, title, onPress, }: { - icon: any; + icon: React.ReactNode; title: string; onPress: () => void; }) { return ( - + {icon} {title} @@ -100,12 +100,12 @@ export default function Home() { {SUB_APPS.map((app) => ( } title={app.title} onPress={() => router.navigate(app.id as any)} /> ))} - router.navigate("/tools")} /> + } title="更多工具" onPress={() => router.navigate("/tools")} /> {/* 填充空位保证对齐 */} {Array.from({ length: Math.max(0, 2 - SUB_APPS.length), diff --git a/code/app/app-rn/src/app/(main)/chat/history.tsx b/code/app/app-rn/src/app/(main)/chat/history.tsx index fda6582c..af528616 100644 --- a/code/app/app-rn/src/app/(main)/chat/history.tsx +++ b/code/app/app-rn/src/app/(main)/chat/history.tsx @@ -21,7 +21,7 @@ const MSG_TYPES = [ { label: "图片", value: ChatMsgType.Image }, { label: "视频", value: ChatMsgType.Video }, { label: "文件", value: ChatMsgType.File }, - { label: "转发", value: ChatMsgType.Forward }, + { label: "卡片", value: ChatMsgType.Forward }, { label: "系统", value: ChatMsgType.System }, ]; diff --git a/code/app/app-rn/src/app/(main)/tools/index.tsx b/code/app/app-rn/src/app/(main)/tools/index.tsx index f517c9c2..99f5740c 100644 --- a/code/app/app-rn/src/app/(main)/tools/index.tsx +++ b/code/app/app-rn/src/app/(main)/tools/index.tsx @@ -1,7 +1,7 @@ import LayoutView from "@/components/layout/view"; import { SearchField, CloseButton, PressableFeedback, useThemeColor } from "heroui-native"; import { toast } from "sonner-native"; -import { View, Text } from "react-native"; +import { Image, View, Text } from "react-native"; import { Stack, useRouter } from "expo-router"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { getGroupedApps, type SubAppItem } from "@/configs/pages"; @@ -109,7 +109,7 @@ export default function Tools() { } }} > - + {/* 收藏分组特殊标记 */} {item.title === "收藏" && ( @@ -129,9 +129,8 @@ export default function Tools() { } return ( - + {item.data.map((subItem) => { - const Icon = subItem.icon; const isFav = favoriteStore.isFavorite(subItem.id); return ( @@ -143,7 +142,7 @@ export default function Tools() { onPress={() => router.navigate(subItem.id as any)} className="flex-row items-center py-3.5 px-4" > - + {subItem.title} diff --git a/code/app/app-rn/src/app/clothes/(tabs)/home/index.tsx b/code/app/app-rn/src/app/clothes/(tabs)/home/index.tsx index e915db66..dafa387b 100644 --- a/code/app/app-rn/src/app/clothes/(tabs)/home/index.tsx +++ b/code/app/app-rn/src/app/clothes/(tabs)/home/index.tsx @@ -112,7 +112,7 @@ export default function ClothingHome() { deleteS3FileQuietly(item.image_file_key); if (deleteRelatedOutfits) state.outfits.filter((o) => relatedIds.includes(o.id)).forEach((o) => deleteS3FileQuietly(o.cover_file_key)); await loadBaseData(); - notifyModuleOperation({ module: "衣服搭配", action: "删除衣服", target: item.name, detail: deleteRelatedOutfits ? `同时删除 ${relatedIds.length} 个关联搭配` : undefined, sourceTable: "b_clothing_clothes", sourceId: item.id }); + notifyModuleOperation({ module: "衣服穿搭", action: "删除衣服", target: item.name, detail: deleteRelatedOutfits ? `同时删除 ${relatedIds.length} 个关联搭配` : undefined, sourceTable: "b_clothing_clothes", sourceId: item.id }); stopLoading({ type: "success", message: "已删除" }); } catch { stopLoading({ type: "error", message: "删除失败" }); } }, [deleteClothesBundle, familyId, isLoading, loadBaseData, notifyModuleOperation, startLoading, stopLoading]); diff --git a/code/app/app-rn/src/app/clothes/(tabs)/outfit/index.tsx b/code/app/app-rn/src/app/clothes/(tabs)/outfit/index.tsx index 78a6e27e..4e55ff78 100644 --- a/code/app/app-rn/src/app/clothes/(tabs)/outfit/index.tsx +++ b/code/app/app-rn/src/app/clothes/(tabs)/outfit/index.tsx @@ -80,7 +80,7 @@ export default function ClothingOutfitList() { const state = useClothingStore.getState(); const details = state.outfitClothes.filter((r) => r.outfit_id === outfit.id); const ok = await saveOutfitBundle({ outfitDeletes: [{ id: outfit.id }], clothesDeletes: details.map((r) => ({ id: r.id })), tagRelationDeletes: state.tagRelations.filter((r) => r.outfit_id === outfit.id).map((r) => ({ id: r.id })) }); if (!ok) { stopLoading({ type: "error", message: "删除失败" }); return; } - await loadData(); notifyModuleOperation({ module: "衣服搭配", action: "删除搭配", target: outfit.name, detail: `${details.length} 件衣服`, sourceTable: "b_clothing_outfit", sourceId: outfit.id }); + await loadData(); notifyModuleOperation({ module: "衣服穿搭", action: "删除搭配", target: outfit.name, detail: `${details.length} 件衣服`, sourceTable: "b_clothing_outfit", sourceId: outfit.id }); stopLoading({ type: "success", message: "已删除" }); } catch { stopLoading({ type: "error", message: "删除失败" }); } }, [deleteConfirm, familyId, isLoading, loadData, notifyModuleOperation, saveOutfitBundle, startLoading, stopLoading]); diff --git a/code/app/app-rn/src/app/clothes/detail/[id]/index.tsx b/code/app/app-rn/src/app/clothes/detail/[id]/index.tsx index 55b5242a..f416c097 100644 --- a/code/app/app-rn/src/app/clothes/detail/[id]/index.tsx +++ b/code/app/app-rn/src/app/clothes/detail/[id]/index.tsx @@ -63,7 +63,7 @@ export default function ClothingDetail() { if (!ok) { stopLoading({ type: "error", message: "删除失败" }); return; } deleteS3FileQuietly(item.image_file_key); state.outfits.filter((o) => relatedIds.includes(o.id)).forEach((o) => deleteS3FileQuietly(o.cover_file_key)); - notifyModuleOperation({ module: "衣服搭配", action: "删除衣服", target: item.name, detail: relatedIds.length > 0 ? `同时删除 ${relatedIds.length} 个关联搭配` : undefined, sourceTable: "b_clothing_clothes", sourceId: item.id }); + notifyModuleOperation({ module: "衣服穿搭", action: "删除衣服", target: item.name, detail: relatedIds.length > 0 ? `同时删除 ${relatedIds.length} 个关联搭配` : undefined, sourceTable: "b_clothing_clothes", sourceId: item.id }); stopLoading({ type: "success", message: "已删除" }); router.replace("/clothes/home" as any); } catch { stopLoading({ type: "error", message: "删除失败" }); } }, [deleteClothesBundle, familyId, isLoading, item, notifyModuleOperation, router, startLoading, stopLoading]); diff --git a/code/app/app-rn/src/app/clothes/edit/index.tsx b/code/app/app-rn/src/app/clothes/edit/index.tsx index 56237291..4d32d306 100644 --- a/code/app/app-rn/src/app/clothes/edit/index.tsx +++ b/code/app/app-rn/src/app/clothes/edit/index.tsx @@ -287,7 +287,7 @@ export default function ClothingEdit() { } await Promise.all([fetchClothes(familyId), fetchTagRelations(familyId)]); notifyModuleOperation({ - module: "衣服搭配", + module: "衣服穿搭", action: isEdit ? "更新衣服" : "新增衣服", target: name, sourceTable: "b_clothing_clothes", diff --git a/code/app/app-rn/src/app/clothes/outfit/detail/[id]/index.tsx b/code/app/app-rn/src/app/clothes/outfit/detail/[id]/index.tsx index 4dd7ed86..484d27cf 100644 --- a/code/app/app-rn/src/app/clothes/outfit/detail/[id]/index.tsx +++ b/code/app/app-rn/src/app/clothes/outfit/detail/[id]/index.tsx @@ -61,7 +61,7 @@ export default function OutfitDetail() { const d = state.outfitClothes.filter((r) => r.outfit_id === item.id); const ok = await saveOutfitBundle({ outfitDeletes: [{ id: item.id }], clothesDeletes: d.map((r) => ({ id: r.id })), tagRelationDeletes: state.tagRelations.filter((r) => r.outfit_id === item.id).map((r) => ({ id: r.id })) }); if (!ok) { stopLoading({ type: "error", message: "删除失败" }); return; } - notifyModuleOperation({ module: "衣服搭配", action: "删除搭配", target: item.name, detail: `${d.length} 件衣服`, sourceTable: "b_clothing_outfit", sourceId: item.id }); + notifyModuleOperation({ module: "衣服穿搭", action: "删除搭配", target: item.name, detail: `${d.length} 件衣服`, sourceTable: "b_clothing_outfit", sourceId: item.id }); stopLoading({ type: "success", message: "已删除" }); router.replace("/clothes/outfit" as any); } catch { stopLoading({ type: "error", message: "删除失败" }); } }, [familyId, isLoading, item, notifyModuleOperation, router, saveOutfitBundle, startLoading, stopLoading]); diff --git a/code/app/app-rn/src/app/clothes/outfit/edit/index.tsx b/code/app/app-rn/src/app/clothes/outfit/edit/index.tsx index e9e1535b..323096a2 100644 --- a/code/app/app-rn/src/app/clothes/outfit/edit/index.tsx +++ b/code/app/app-rn/src/app/clothes/outfit/edit/index.tsx @@ -648,7 +648,7 @@ export default function ClothingOutfitEdit() { fetchTagRelations(familyId), ]); notifyModuleOperation({ - module: "衣服搭配", + module: "衣服穿搭", action: isEdit ? "更新搭配" : "新增搭配", target: trimmedName, detail: `${sortedItems.length} 件衣服`, diff --git a/code/app/app-rn/src/app/meal/(tabs)/dishes/index.tsx b/code/app/app-rn/src/app/meal/(tabs)/dishes/index.tsx index 3a47cfb5..2080656b 100644 --- a/code/app/app-rn/src/app/meal/(tabs)/dishes/index.tsx +++ b/code/app/app-rn/src/app/meal/(tabs)/dishes/index.tsx @@ -1,9 +1,11 @@ import { useCallback, useEffect, useMemo, useState } from "react"; import { Dimensions, FlatList, ScrollView, Text, TouchableOpacity, View } from "react-native"; import { router, useFocusEffect } from "expo-router"; +import dayjs from "dayjs"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { Button, Dialog, Label, TextField } from "heroui-native"; import { CheckIcon, FilterIcon, PencilIcon, PlusIcon, RotateCcwIcon, Trash2Icon } from "lucide-react-native"; +import { toast } from "sonner-native"; import BottomSheet from "@/components/BottomSheet"; import ContextMenu from "@/components/ContextMenu"; import EmptyState from "@/components/EmptyStatus"; @@ -11,7 +13,7 @@ import LayoutView from "@/components/layout/view"; import MealDishImage from "@/components/meal/MealDishImage"; import { LoadingOverlay } from "@/components/LoadingOverlay"; import { useListLoading, useLoading } from "@/hooks/useLoading"; -import { useFamilyStore, useMealStore } from "@/store"; +import { useFamilyStore, useMealStore, useUserStore } from "@/store"; import { MealDish, MealStatus, MealTag } from "@/types/meal"; // ============================================================ @@ -36,14 +38,20 @@ const MODULE_HEADER_H = 50; // 模块顶部 header 高度(对齐 LayoutView export default function MealDishesPage() { const familyId = useFamilyStore((s) => s.getFamilyId()); + const userId = useUserStore((s) => s.getUserId()); const dishes = useMealStore((s) => s.dishes); const categories = useMealStore((s) => s.categories); const tags = useMealStore((s) => s.tags); const dishTags = useMealStore((s) => s.dishTags); + const slots = useMealStore((s) => s.slots); const fetchDishes = useMealStore((s) => s.fetchDishes); const fetchCategories = useMealStore((s) => s.fetchCategories); const fetchTags = useMealStore((s) => s.fetchTags); const fetchDishTags = useMealStore((s) => s.fetchDishTags); + const fetchSlots = useMealStore((s) => s.fetchSlots); + const fetchPlansByDate = useMealStore((s) => s.fetchPlansByDate); + const createEmptyPlan = useMealStore((s) => s.createEmptyPlan); + const addDishToPlan = useMealStore((s) => s.addDishToPlan); const saveDishChanges = useMealStore((s) => s.saveDishChanges); const { isLoading, startLoading, stopLoading } = useLoading(); const { refreshing, runInitial, runRefresh } = useListLoading(); @@ -51,6 +59,10 @@ export default function MealDishesPage() { const insets = useSafeAreaInsets(); const screenH = Dimensions.get("window").height; + // 日期常量 + const today = dayjs().format("YYYY-MM-DD"); + const tomorrow = dayjs().add(1, "day").format("YYYY-MM-DD"); + // 筛选状态 const [keyword, setKeyword] = useState(""); const [filter, setFilter] = useState(createDefaultFilter); @@ -63,12 +75,17 @@ export default function MealDishesPage() { const [menuPos, setMenuPos] = useState({ x: 0, y: 0 }); const [deleteConfirm, setDeleteConfirm] = useState(null); + // 加入安排 + const [addDishTarget, setAddDishTarget] = useState(null); + const [addDishDate, setAddDishDate] = useState(today); + // 左右两栏可用高度 const bodyH = screenH - insets.top - MODULE_HEADER_H - HEADER_H - TAB_BAR_H - insets.bottom; // ======== 派生数据 ======== const enabledCats = useMemo(() => categories.filter((c) => c.status !== 0), [categories]); + const enabledSlots = useMemo(() => slots.filter((s) => s.status !== 0).sort((a, b) => a.sort_number - b.sort_number), [slots]); const activeFilterCount = useMemo(() => { let c = 0; if (filter.categoryId) c++; if (filter.tagIds.length > 0) c++; return c; @@ -99,8 +116,8 @@ export default function MealDishesPage() { const loadData = useCallback(async () => { if (!familyId) return; - await Promise.all([fetchDishes(familyId), fetchCategories(familyId), fetchTags(familyId), fetchDishTags(familyId)]); - }, [familyId, fetchCategories, fetchDishTags, fetchDishes, fetchTags]); + await Promise.all([fetchDishes(familyId), fetchCategories(familyId), fetchTags(familyId), fetchDishTags(familyId), fetchSlots(familyId)]); + }, [familyId, fetchCategories, fetchDishTags, fetchDishes, fetchTags, fetchSlots]); useEffect(() => { if (familyId) runInitial(loadData); }, [familyId]); useFocusEffect(useCallback(() => { if (familyId) runRefresh(loadData); }, [familyId, loadData, runRefresh])); @@ -132,6 +149,50 @@ export default function MealDishesPage() { return setDraftFilter((p) => ({ ...p, tagIds: p.tagIds.includes(t.id) ? p.tagIds.filter((id) => id !== t.id) : [...p.tagIds, t.id] }))}>{t.name}; }, [draftFilter.tagIds]); + // ======== 加入安排 ======== + + const handleAddToSlot = useCallback(async (slotId: number, slotName: string) => { + if (!familyId || !userId || !addDishTarget) return; + const dish = addDishTarget; + const date = addDishDate; + setAddDishTarget(null); + startLoading("添加中..."); + try { + // 获取该日期的安排 + await fetchPlansByDate(familyId, date); + const plans = useMealStore.getState().plans; + let planId = plans.find((p) => p.meal_slot_id === slotId)?.id; + + // 没有安排则创建 + if (!planId) { + planId = await createEmptyPlan({ familyId, planDate: date, mealSlotId: slotId, userId }); + } + if (!planId) { + stopLoading({ type: "error", message: "创建安排失败" }); + return; + } + + // 添加菜品 + const result = await addDishToPlan({ + familyId, + planId, + dishId: dish.id, + dishName: dish.name, + userId, + sortNumber: 0, + }); + if (result.planItemId) { + const dateLabel = date === today ? "今天" : date === tomorrow ? "明天" : date; + toast.success(`已加入${dateLabel}${slotName}`); + stopLoading(); + } else { + stopLoading({ type: "error", message: "添加失败" }); + } + } catch { + stopLoading({ type: "error", message: "添加失败" }); + } + }, [familyId, userId, addDishTarget, addDishDate, today, tomorrow, fetchPlansByDate, createEmptyPlan, addDishToPlan, startLoading, stopLoading]); + // ======== 渲染 ======== return ( @@ -227,6 +288,14 @@ export default function MealDishesPage() { {!!item.cooking_time_minutes && 约{item.cooking_time_minutes}分钟} + { setAddDishDate(today); setAddDishTarget(item); }} + > + + {">"} )} @@ -250,6 +319,52 @@ export default function MealDishesPage() { + {/* ======== 加入安排 BottomSheet ======== */} + { if (!v) setAddDishTarget(null); }}> + + + + 加入安排{addDishTarget ? ` · ${addDishTarget.name}` : ""} + + + {/* 日期选择 */} + + setAddDishDate(today)} + > + 今天 + + setAddDishDate(tomorrow)} + > + 明天 + + + + {/* 餐次选择 */} + + 选择餐次 + {enabledSlots.length > 0 ? ( + + {enabledSlots.map((slot) => ( + handleAddToSlot(slot.id, slot.name)} + > + {slot.name} + + ))} + + ) : ( + 暂无餐次,请先在设置中添加 + )} + + + + {/* ======== 长按菜单 ======== */} setActiveItem(null)} items={[ diff --git a/code/app/app-rn/src/app/meal/dish/edit/index.tsx b/code/app/app-rn/src/app/meal/dish/edit/index.tsx index b3f93b07..96ba825e 100644 --- a/code/app/app-rn/src/app/meal/dish/edit/index.tsx +++ b/code/app/app-rn/src/app/meal/dish/edit/index.tsx @@ -110,14 +110,14 @@ export default function MealDishEdit() { if (isEdit) { const ok = await saveDishChanges({ updates: [{ id: editId, name: form.name.trim(), image_file_key: imageFileKey, category_id: form.categoryId, difficulty: form.difficulty, cooking_time_minutes: form.cookingTime ? Number(form.cookingTime) : 0, remark: form.remark.trim() }] }); if (!ok) { stopLoading({ type: "error", message: "保存失败" }); return; } - notifyOp({ module: "餐食", action: "编辑菜品", target: form.name.trim(), sourceTable: "b_meal_dish", sourceId: editId }); + notifyOp({ module: "家庭菜单", action: "编辑菜品", target: form.name.trim(), sourceTable: "b_meal_dish", sourceId: editId }); } else { const sc = steps.length + 1; const idsRes = await getUniqueIdApi(sc); const dishId = idsRes.data?.[0] ?? 0; if (!dishId) { stopLoading({ type: "error", message: "创建失败" }); return; } const sids2 = idsRes.data?.slice(1) ?? []; const sis2 = steps.map((s, i) => ({ id: sids2[i] ?? 0, family_id: familyId, dish_id: dishId, step_number: s.stepNumber, description: s.description, image_file_key: s.imageFileKey, sort_number: i })); const ok = await saveDishBundle({ dishInserts: [{ id: dishId, family_id: familyId, name: form.name.trim(), image_file_key: imageFileKey, category_id: form.categoryId, difficulty: form.difficulty, cooking_time_minutes: form.cookingTime ? Number(form.cookingTime) : 0, remark: form.remark.trim(), status: MealStatus.Enabled, sort_number: 0 }], dishStepInserts: sis2 }); if (!ok) { stopLoading({ type: "error", message: "保存失败" }); return; } - notifyOp({ module: "餐食", action: "新增菜品", target: form.name.trim(), sourceTable: "b_meal_dish", sourceId: dishId }); + notifyOp({ module: "家庭菜单", action: "新增菜品", target: form.name.trim(), sourceTable: "b_meal_dish", sourceId: dishId }); } stopLoading({ type: "success", message: "已保存" }); router.back(); diff --git a/code/app/app-rn/src/app/meal/plan/detail/[id]/index.tsx b/code/app/app-rn/src/app/meal/plan/detail/[id]/index.tsx index cf44e1e4..93182239 100644 --- a/code/app/app-rn/src/app/meal/plan/detail/[id]/index.tsx +++ b/code/app/app-rn/src/app/meal/plan/detail/[id]/index.tsx @@ -357,7 +357,7 @@ export default function MealPlanDetail() { setComment(""); await loadData(); notifyOp({ - module: "餐食", + module: "家庭菜单", action: "评论菜单", target: planId.toString(), sourceTable: "b_meal_plan_log", diff --git a/code/app/app-rn/src/app/meal/plan/edit/index.tsx b/code/app/app-rn/src/app/meal/plan/edit/index.tsx index b075819d..acff4939 100644 --- a/code/app/app-rn/src/app/meal/plan/edit/index.tsx +++ b/code/app/app-rn/src/app/meal/plan/edit/index.tsx @@ -120,7 +120,7 @@ export default function MealPlanEdit() { } notifyOp({ - module: "餐食", + module: "家庭菜单", action: "创建菜单", target: `${planDate} ${slots.find((s) => s.id === mealSlotId)?.name ?? ""}`, sourceTable: "b_meal_plan", diff --git a/code/app/app-rn/src/configs/pages.ts b/code/app/app-rn/src/configs/pages.ts index e979b83e..d3fd7bf0 100644 --- a/code/app/app-rn/src/configs/pages.ts +++ b/code/app/app-rn/src/configs/pages.ts @@ -1,9 +1,12 @@ -import { IconAppFinance, IconAppClothes, IconAppMeal, IconAppTodo, IconAppAnniversary } from "@/components/iconfont"; +import type { ImageSourcePropType } from "react-native"; +import JizhangIcon from "@/static/images/subapps/jizhang.webp"; +import CaidanIcon from "@/static/images/subapps/caidan.webp"; +import YifudapeiIcon from "@/static/images/subapps/yifudapei.webp"; export interface SubAppItem { id: string; title: string; - icon: any; + icon: ImageSourcePropType; group: string; description?: string; } @@ -25,21 +28,21 @@ export const SUB_APPS: SubAppItem[] = [ { id: "/finance/home", title: "记账", - icon: IconAppFinance, + icon: JizhangIcon, group: "J", description: "记录日常收支,管理你的每一笔钱", }, { id: "/clothes/home", - title: "衣服搭配", - icon: IconAppClothes, + title: "衣服穿搭", + icon: YifudapeiIcon, group: "Y", description: "帮你整理每日穿搭灵感", }, { id: "/meal/home", - title: "餐食", - icon: IconAppMeal, + title: "家庭菜单", + icon: CaidanIcon, group: "J", description: "家庭菜品库与餐食安排", }, @@ -47,7 +50,7 @@ export const SUB_APPS: SubAppItem[] = [ // { // id: "/todo/home", // title: "家庭待办", - // icon: IconAppTodo, + // icon: require("@/static/images/subapps/daiban.webp"), // group: "J", // description: "家庭任务分配与跟踪", // }, @@ -55,7 +58,7 @@ export const SUB_APPS: SubAppItem[] = [ // { // id: "/anniversary/home", // title: "纪念日", - // icon: IconAppAnniversary, + // icon: require("@/static/images/subapps/jinianri.webp"), // group: "J", // description: "记录家人的生日与重要纪念日", // }, diff --git a/code/app/app-rn/src/hooks/useModuleChatNotifier.ts b/code/app/app-rn/src/hooks/useModuleChatNotifier.ts index 509748b9..7884af54 100644 --- a/code/app/app-rn/src/hooks/useModuleChatNotifier.ts +++ b/code/app/app-rn/src/hooks/useModuleChatNotifier.ts @@ -24,7 +24,7 @@ export function useModuleChatNotifier() { chat.send({ type: "send", - msg_type: ChatMsgType.System, + msg_type: ChatMsgType.Forward, content, source_table: sourceTable, source_id: sourceId, diff --git a/code/app/app-rn/src/static/images/subapps/caidan.png b/code/app/app-rn/src/static/images/subapps/caidan.png new file mode 100644 index 0000000000000000000000000000000000000000..b169d3a2506aa96cc4132323629ef55a3c0730ae GIT binary patch literal 5380 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 ioB#j-21!IgR09Boo1 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 u@%+cb#kKeu8=KxYG?dFLq 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 literal 0 HcmV?d00001 diff --git a/code/app/app-rn/src/static/images/subapps/jizhang.webp b/code/app/app-rn/src/static/images/subapps/jizhang.webp new file mode 100644 index 0000000000000000000000000000000000000000..00609171a4047e8e0e91d62d7e187f806640176d GIT binary patch literal 7504 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 a%d7l7Iw3?sFbvs_N2JhWAOHXW0001#hrNja literal 0 HcmV?d00001 diff --git a/code/app/app-rn/src/static/images/subapps/yifudapei.png b/code/app/app-rn/src/static/images/subapps/yifudapei.png new file mode 100644 index 0000000000000000000000000000000000000000..5471785b499f476cccb3ceb1c6261c64beae7dc5 GIT binary patch literal 10031 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 M0ssI20000005Ps)cK`qY literal 0 HcmV?d00001 diff --git a/code/app/app-rn/src/types/chat.ts b/code/app/app-rn/src/types/chat.ts index d576d02a..b9a97cd4 100644 --- a/code/app/app-rn/src/types/chat.ts +++ b/code/app/app-rn/src/types/chat.ts @@ -8,9 +8,9 @@ export enum ChatMsgType { Video = 2, /** 文件 */ File = 3, - /** 转发(模块内容分享) */ + /** 模块卡片(记账、待办、餐食等操作动态,以卡片形式展示) */ Forward = 4, - /** 系统消息(加入家庭、改名、记账、收藏等) */ + /** 系统消息(加入家庭、改名等纯通知) */ System = 5, } diff --git a/code/app/app-rn/src/types/global.d.ts b/code/app/app-rn/src/types/global.d.ts index 1ee6356a..07daad06 100644 --- a/code/app/app-rn/src/types/global.d.ts +++ b/code/app/app-rn/src/types/global.d.ts @@ -1,5 +1,23 @@ export {}; +declare module "@/static/images/subapps/jizhang.webp" { + import { ImageSourcePropType } from "react-native"; + const content: ImageSourcePropType; + export default content; +} + +declare module "@/static/images/subapps/caidan.webp" { + import { ImageSourcePropType } from "react-native"; + const content: ImageSourcePropType; + export default content; +} + +declare module "@/static/images/subapps/yifudapei.webp" { + import { ImageSourcePropType } from "react-native"; + const content: ImageSourcePropType; + export default content; +} + declare global { enum HttpReponseCode { Success = 1000, diff --git a/code/app/app-rn/tsconfig.json b/code/app/app-rn/tsconfig.json index afa1230b..ddb8498b 100644 --- a/code/app/app-rn/tsconfig.json +++ b/code/app/app-rn/tsconfig.json @@ -11,6 +11,7 @@ "**/*.ts", "**/*.tsx", "./src/uniwind-types.d.ts", + "./src/types/global.d.ts", "env.js", ".expo/types/**/*.ts", "expo-env.d.ts",