20260705214103

This commit is contained in:
oneao committed 2026-07-05 21:41:03 +08:00
1 parent 3556d5e8c1
commit ae99f50f6d
27 files changed
+2945 -3292

No files matched your search

+59 -3
View File
@@ -1,5 +1,5 @@
import { create } from "zustand";
import { loadDataApi, saveDataApi, s3UploadFileApi } from "@/request/api";
import { loadDataApi, loadDataPageApi, saveDataApi, s3UploadFileApi } from "@/request/api";
import {
ClothingCategory,
ClothingClothes,
@@ -36,6 +36,8 @@ interface ClothingState {
tags: ClothingTag[];
tagRelations: ClothingTagRelation[];
members: ClothingFamilyMember[];
clothesTotal: number;
outfitsTotal: number;
clear: () => void;
@@ -52,6 +54,8 @@ interface ClothingState {
) => Promise<void>;
fetchMembers: (familyId: number) => Promise<void>;
getOwnerName: (ownerId?: number | null) => string;
loadMoreClothes: (familyId: number) => Promise<boolean>;
loadMoreOutfits: (familyId: number) => Promise<boolean>;
saveClothesChanges: (changes: ClothingSaveChanges<ClothingClothes>) => Promise<boolean>;
saveOutfitChanges: (changes: ClothingSaveChanges<ClothingOutfit>) => Promise<boolean>;
@@ -126,6 +130,8 @@ export const useClothingStore = create<ClothingState>((set, get) => ({
tags: [],
tagRelations: [],
members: [],
clothesTotal: 0,
outfitsTotal: 0,
clear: () =>
set({
@@ -136,6 +142,8 @@ export const useClothingStore = create<ClothingState>((set, get) => ({
tags: [],
tagRelations: [],
members: [],
clothesTotal: 0,
outfitsTotal: 0,
}),
fetchAll: async (familyId: number) => {
@@ -281,9 +289,9 @@ export const useClothingStore = create<ClothingState>((set, get) => ({
if (!familyId) return;
const res = await loadDataApi({
view_name: TABLES.familyMember,
view_name: "v_family_member",
search_condition: "family_id = ?",
search_columns: ["user_id", "nickname"],
search_columns: ["user_id", "nickname", "avatar"],
order_by: "id asc",
args: [familyId],
});
@@ -299,6 +307,54 @@ export const useClothingStore = create<ClothingState>((set, get) => ({
return m?.nickname || `用户${ownerId}`;
},
loadMoreClothes: async (familyId: number) => {
if (!familyId) return false;
const state = get();
const page = Math.floor(state.clothes.length / 20) + 1;
const res = await loadDataPageApi({
view_name: TABLES.clothes,
search_condition: "family_id = ? and status = ?",
search_columns: [],
order_by: "sort_number asc, id asc",
args: [familyId, ClothingStatus.Enabled],
page,
page_size: 20,
});
if (res.isSuccess) {
const { rows, total } = res.data;
set({
clothes: page === 1 ? rows : [...state.clothes, ...rows],
clothesTotal: total,
});
return state.clothes.length + rows.length < total;
}
return false;
},
loadMoreOutfits: async (familyId: number) => {
if (!familyId) return false;
const state = get();
const page = Math.floor(state.outfits.length / 20) + 1;
const res = await loadDataPageApi({
view_name: TABLES.outfit,
search_condition: "family_id = ? and status = ?",
search_columns: [],
order_by: "sort_number asc, id asc",
args: [familyId, ClothingStatus.Enabled],
page,
page_size: 20,
});
if (res.isSuccess) {
const { rows, total } = res.data;
set({
outfits: page === 1 ? rows : [...state.outfits, ...rows],
outfitsTotal: total,
});
return state.outfits.length + rows.length < total;
}
return false;
},
saveClothesChanges: async (changes: ClothingSaveChanges<ClothingClothes>) => {
const res = await saveDataApi([
{ table_name: TABLES.clothes, key_field: "id", ...normalizeChanges(changes) },
+159 -1
View File
@@ -1,5 +1,5 @@
import { create } from "zustand";
import { loadDataApi, saveDataApi, s3UploadFileApi } from "@/request/api";
import { loadDataApi, saveDataApi, s3UploadFileApi, getUniqueIdApi } from "@/request/api";
import {
MealCategory,
MealDish,
@@ -9,6 +9,8 @@ import {
MealPlan,
MealPlanItem,
MealPlanLog,
MealPlanLogType,
MealPlanStatus,
MealSaveChanges,
MealSlot,
MealStatus,
@@ -98,6 +100,13 @@ interface MealState {
// 家庭动态(占位,待 family feed API 就绪后接入)
recordFeed: (familyId: number, content: string) => void;
// 菜单事件驱动操作
createEmptyPlan: (params: { familyId: number; planDate: string; mealSlotId: number; userId: number }) => Promise<number | null>;
confirmPlan: (params: { planId: number; familyId: number; userId: number }) => Promise<boolean>;
completePlan: (params: { planId: number; familyId: number; userId: number }) => Promise<boolean>;
addDishToPlan: (params: { familyId: number; planId: number; dishId: number; dishName: string; userId: number; sortNumber: number }) => Promise<{ planItemId?: number; logId?: number }>;
removeDishFromPlan: (params: { familyId: number; planId: number; planItemId: number; dishName: string; userId: number }) => Promise<boolean>;
}
function normalizeChanges<T>(changes: MealSaveChanges<T>) {
@@ -474,4 +483,153 @@ export const useMealStore = create<MealState>((set, get) => ({
// TODO: 接入 family feed API 后实现
// 加菜(type=2)和留下备注(type=4)两个动作触发
},
// ======== 菜单事件驱动操作 ========
// 创建空菜单(不需要预先选择菜品)
createEmptyPlan: async ({ familyId, planDate, mealSlotId, userId }) => {
if (!familyId) return null;
const idRes = await getUniqueIdApi(1);
const planId = idRes.data?.[0] ?? 0;
if (!planId) return null;
const logIdRes = await getUniqueIdApi(1);
const logId = logIdRes.data?.[0] ?? 0;
const now = new Date().toISOString();
const ok = await get().savePlanBundle({
planInserts: [{
id: planId,
family_id: familyId,
plan_date: planDate,
meal_slot_id: mealSlotId,
status: MealPlanStatus.Planning,
create_time: now,
update_time: now,
create_by: userId,
update_by: userId,
}],
planUpdates: [],
planItemInserts: [],
planItemUpdates: [],
planItemDeletes: [],
planLogInserts: logId ? [{
id: logId,
family_id: familyId,
plan_id: planId,
type: MealPlanLogType.Plan,
create_by: userId,
create_time: now,
}] : [],
});
if (ok) {
// 将新创建的 plan 添加到本地状态
const newPlan: MealPlan = {
id: planId,
family_id: familyId,
plan_date: planDate,
meal_slot_id: mealSlotId,
status: MealPlanStatus.Planning,
create_time: now,
update_time: now,
create_by: userId,
update_by: userId,
};
set((s) => ({ plans: [...s.plans, newPlan] }));
return planId;
}
return null;
},
// 确认菜单(菜单已定,但还没做)
confirmPlan: async ({ planId, familyId, userId }) => {
if (!familyId || !planId) return false;
const now = new Date().toISOString();
return get().savePlanChanges({
updates: [{
id: planId,
status: MealPlanStatus.Confirmed,
update_time: now,
update_by: userId,
}],
});
},
// 完成菜单(已做完,锁定不可修改)
completePlan: async ({ planId, familyId, userId }) => {
if (!familyId || !planId) return false;
const now = new Date().toISOString();
return get().savePlanChanges({
updates: [{
id: planId,
status: MealPlanStatus.Completed,
update_time: now,
update_by: userId,
}],
});
},
// 添加菜品到菜单(事件驱动:写入 plan_item + plan_log)
addDishToPlan: async ({ familyId, planId, dishId, dishName, userId, sortNumber }) => {
if (!familyId || !planId) return {};
const [idRes, logIdRes] = await Promise.all([getUniqueIdApi(1), getUniqueIdApi(1)]);
const itemId = idRes.data?.[0] ?? 0;
const logId = logIdRes.data?.[0] ?? 0;
if (!itemId) return {};
const now = new Date().toISOString();
const ops: any[] = [
{
table_name: TABLES.planItem,
key_field: "id",
inserts: [{ id: itemId, family_id: familyId, plan_id: planId, dish_id: dishId, quantity: 1, sort_number: sortNumber, create_time: now, create_by: userId }],
updates: [],
deletes: [],
},
];
if (logId) {
ops.push({
table_name: TABLES.planLog,
key_field: "id",
inserts: [{ id: logId, family_id: familyId, plan_id: planId, type: MealPlanLogType.AddDish, ref_id: itemId, dish_name: dishName, create_by: userId, create_time: now }],
updates: [],
deletes: [],
});
}
const res = await saveDataApi(ops);
if (res.isSuccess) {
get().recordFeed(familyId, JSON.stringify({ type: "meal_add_dish", plan_id: planId, dish_name: dishName }));
}
return res.isSuccess ? { planItemId: itemId, logId: logId || undefined } : {};
},
// 移除菜品(事件驱动:删除 plan_item + 写入 plan_log)
removeDishFromPlan: async ({ familyId, planId, planItemId, dishName, userId }) => {
if (!familyId || !planId || !planItemId) return false;
const logIdRes = await getUniqueIdApi(1);
const logId = logIdRes.data?.[0] ?? 0;
const now = new Date().toISOString();
const ops: any[] = [
{
table_name: TABLES.planItem,
key_field: "id",
inserts: [],
updates: [],
deletes: [{ id: planItemId }],
},
];
if (logId) {
ops.push({
table_name: TABLES.planLog,
key_field: "id",
inserts: [{ id: logId, family_id: familyId, plan_id: planId, type: MealPlanLogType.RemoveDish, ref_id: planItemId, dish_name: dishName, create_by: userId, create_time: now }],
updates: [],
deletes: [],
});
}
const res = await saveDataApi(ops);
return res.isSuccess;
},
}));