import type { FeedingPlanProduct } from '@/model/feeding-plan' import type { Product } from '@/model/product' import { createFeedingPlan } from '@/api/feeding-plan' import { createPet } from '@/api/pet' import { type CreateFeedingPlanRequest, type CreatePetRequest, FeedingGoal, Gender, type Pet, PetBodyType, PetType, } from '@/model/pet' import { defineStore } from 'pinia' export const useFeedingPlanStore = defineStore('feeding-plan', () => { const petTypeOptions = [ { value: PetType.CAT, label: '猫猫' }, { value: PetType.DOG, label: '狗狗' }, ] const genderOptions = [ { value: Gender.MALE, label: '男孩' }, { value: Gender.FEMALE, label: '女孩' }, ] const dailyCalories = ref(400) const pet = ref ({ birthday: new Date().toISOString().split('T')[0], bodyType: PetBodyType.IDEAL, gender: Gender.MALE, isActive: false, isLactation: false, isPregnant: false, isSterilization: false, name: '子龙', photo: '', type: PetType.CAT, weight: 0, }) const savedPet = ref(null) const petTags = computed(() => { return [ { key: 'age', value: `${new Date().getFullYear() - new Date(pet.value.birthday).getFullYear()}岁` }, { key: 'type', value: pet.value.type === PetType.CAT ? '猫猫' : '狗狗' }, { key: 'weight', value: `${pet.value.weight}kg` }, ] }) const feedingPlan = ref({ feedingGoal: FeedingGoal.LOSE, targetWeight: pet.value.weight - 1, petId: savedPet.value?.id || '', products: [], }) const feedingGoalOptions = [ { value: FeedingGoal.GAIN, label: '增肥' }, { value: FeedingGoal.LOSE, label: '减重' }, { value: FeedingGoal.MAINTAIN, label: '保持' }, ] const setPetValue = (key: keyof CreatePetRequest, value: any) => { // @ts-expect-error @ts-ignore pet.value[key] = (value as CreatePetRequest[keyof CreatePetRequest]) } const persistentPet = async () => { savedPet.value = await createPet({ ...pet.value, }) } /** * 用户选中的商品列表,每个含有 product 信息和 percentage 百分比 */ const selectedProducts = ref([]) const arrangeDailyConsumeWeight = () => { selectedProducts.value.forEach((item) => { item.dailyUsageWeight = Math.floor((dailyCalories.value * item.percentage / 100) / item.product.totalCalories * item.product.totalWeight) }) } /** * 将所有已选商品的 percentage【平分】为 100 / total, * 并用 Math.floor 取整后,将多余 remainder 逐个分配(防止总和小于 100)。 */ function recalculatePercentage() { const total = selectedProducts.value.length if (total === 0) return // 1. 先把 100 整除 const base = Math.floor(100 / total) // 每个至少分到多少 const remainder = 100 - base * total // 还剩多少未分配 // 2. 所有人先设置为 base selectedProducts.value.forEach((item) => { item.percentage = base }) // 3. 余数 remainder 逐个+1 分配给前 remainder 个 // 如果你不在意 1% 的误差,可以省略这一步 for (let i = 0; i < remainder; i++) { selectedProducts.value[i].percentage += 1 } } /** * 添加一个新商品 * - 如果已存在则直接 return * - 如果不存在则 push 到 selectedProducts,并平均分配 */ function addProductToSelected(product: Product) { // 如果已存在,不重复添加 if (selectedProducts.value.some(item => item.product.id === product.id)) { return } // 新商品默认 0%(稍后 re calc 会统一分配) selectedProducts.value.push({ product, percentage: 0, dailyUsageWeight: 0, }) // 重新平分所有商品的比例 recalculatePercentage() arrangeDailyConsumeWeight() } /** * 给除 exceptId 之外的商品,分摊 -delta; * 也就是说,如果 delta 是正,其他商品就要减掉 delta;如果 delta 是负,其他商品就要加上 -delta。 * * 注意:这里用 `-=` 实现把 diff 分摊给其他产品 */ function modifyPercentageExceptId(exceptId: string, delta: number) { selectedProducts.value.forEach((item) => { if (item.product.id !== exceptId) { item.percentage -= delta } }) } /** * 修改指定 productId 的 percentage * @param productId 产品的id * @param newVal 用户想要设置的新值 (0~100) */ function changePercentage(productId: string, newVal: number) { const product = selectedProducts.value.find(item => item.product.id === productId) if (product) { const oldVal = product.percentage const diff = newVal - oldVal // 先更新本产品的新值 product.percentage = newVal // 计算需要从(或给)其他商品分配的增量 // total-1 防止只有一个商品时出现除以0 const total = selectedProducts.value.length if (total > 1) { const deltaPerProduct = diff / (total - 1) // 其他商品分摊 -deltaPerProduct (实际在函数里 -= deltaPerProduct) modifyPercentageExceptId(productId, deltaPerProduct) } arrangeDailyConsumeWeight() } } const confirm = async () => { if (savedPet.value === null) { return } feedingPlan.value.petId = savedPet.value.id feedingPlan.value.products = selectedProducts.value.map((item) => { return { id: item.product.id, dailyUsageWeight: item.dailyUsageWeight, } }) await createFeedingPlan(feedingPlan.value) } return { pet, petTypeOptions, genderOptions, setPetValue, persistentPet, petTags, feedingPlan, feedingGoalOptions, selectedProducts, addProductToSelected, changePercentage, dailyCalories, confirm, } })