20260705214103
This commit is contained in:
1 parent
3556d5e8c1
commit
ae99f50f6d
27 files changed
+2945
-3292
No files matched your search
@@ -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) },
|
||||
|
||||
@@ -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;
|
||||
},
|
||||
}));
|
||||
Reference in new issue
Block a user