| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203 |
- 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 <CreatePetRequest>({
- 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<Pet | null>(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<CreateFeedingPlanRequest>({
- 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<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 {
- pet,
- petTypeOptions,
- genderOptions,
- setPetValue,
- persistentPet,
- petTags,
- feedingPlan,
- feedingGoalOptions,
- selectedProducts,
- addProductToSelected,
- changePercentage,
- dailyCalories,
- confirm,
- }
- })
|