|
@@ -1,9 +1,13 @@
|
|
|
|
|
+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 { createPet } from '@/api/pet'
|
|
|
import {
|
|
import {
|
|
|
type CreateFeedingPlanRequest,
|
|
type CreateFeedingPlanRequest,
|
|
|
type CreatePetRequest,
|
|
type CreatePetRequest,
|
|
|
FeedingGoal,
|
|
FeedingGoal,
|
|
|
Gender,
|
|
Gender,
|
|
|
|
|
+ type Pet,
|
|
|
PetBodyType,
|
|
PetBodyType,
|
|
|
PetType,
|
|
PetType,
|
|
|
} from '@/model/pet'
|
|
} from '@/model/pet'
|
|
@@ -20,6 +24,8 @@ export const useFeedingPlanStore = defineStore('feeding-plan', () => {
|
|
|
{ value: Gender.FEMALE, label: '女孩' },
|
|
{ value: Gender.FEMALE, label: '女孩' },
|
|
|
]
|
|
]
|
|
|
|
|
|
|
|
|
|
+ const dailyCalories = ref(400)
|
|
|
|
|
+
|
|
|
const pet = ref <CreatePetRequest>({
|
|
const pet = ref <CreatePetRequest>({
|
|
|
birthday: new Date().toISOString().split('T')[0],
|
|
birthday: new Date().toISOString().split('T')[0],
|
|
|
bodyType: PetBodyType.IDEAL,
|
|
bodyType: PetBodyType.IDEAL,
|
|
@@ -34,6 +40,8 @@ export const useFeedingPlanStore = defineStore('feeding-plan', () => {
|
|
|
weight: 0,
|
|
weight: 0,
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
|
|
+ const savedPet = ref<Pet | null>(null)
|
|
|
|
|
+
|
|
|
const petTags = computed(() => {
|
|
const petTags = computed(() => {
|
|
|
return [
|
|
return [
|
|
|
{ key: 'age', value: `${new Date().getFullYear() - new Date(pet.value.birthday).getFullYear()}岁` },
|
|
{ key: 'age', value: `${new Date().getFullYear() - new Date(pet.value.birthday).getFullYear()}岁` },
|
|
@@ -45,6 +53,8 @@ export const useFeedingPlanStore = defineStore('feeding-plan', () => {
|
|
|
const feedingPlan = ref<CreateFeedingPlanRequest>({
|
|
const feedingPlan = ref<CreateFeedingPlanRequest>({
|
|
|
feedingGoal: FeedingGoal.LOSE,
|
|
feedingGoal: FeedingGoal.LOSE,
|
|
|
targetWeight: pet.value.weight - 1,
|
|
targetWeight: pet.value.weight - 1,
|
|
|
|
|
+ petId: savedPet.value?.id || '',
|
|
|
|
|
+ products: [],
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
const feedingGoalOptions = [
|
|
const feedingGoalOptions = [
|
|
@@ -59,11 +69,122 @@ export const useFeedingPlanStore = defineStore('feeding-plan', () => {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const persistentPet = async () => {
|
|
const persistentPet = async () => {
|
|
|
- await createPet({
|
|
|
|
|
|
|
+ savedPet.value = await createPet({
|
|
|
...pet.value,
|
|
...pet.value,
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 用户选中的商品列表,每个含有 product 信息和 percentage 百分比
|
|
|
|
|
+ */
|
|
|
|
|
+ const selectedProducts = ref<FeedingPlanProduct[]>([])
|
|
|
|
|
+
|
|
|
|
|
+ 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 {
|
|
return {
|
|
|
pet,
|
|
pet,
|
|
|
petTypeOptions,
|
|
petTypeOptions,
|
|
@@ -73,5 +194,10 @@ export const useFeedingPlanStore = defineStore('feeding-plan', () => {
|
|
|
petTags,
|
|
petTags,
|
|
|
feedingPlan,
|
|
feedingPlan,
|
|
|
feedingGoalOptions,
|
|
feedingGoalOptions,
|
|
|
|
|
+ selectedProducts,
|
|
|
|
|
+ addProductToSelected,
|
|
|
|
|
+ changePercentage,
|
|
|
|
|
+ dailyCalories,
|
|
|
|
|
+ confirm,
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|