瀏覽代碼

feat: 宠物档案管理

IlhamTahir 11 月之前
父節點
當前提交
a55a6d3586

+ 5 - 1
src/pages/feed-calculator/components/FeedQuestionnaire.vue

@@ -67,6 +67,10 @@ function handleAnswer(answer: PetCard) {
     step.value += 1
   }
 }
+
+async function confirm() {
+  await feedingPlanStore.persistentPet()
+}
 </script>
 
 <template>
@@ -98,7 +102,7 @@ function handleAnswer(answer: PetCard) {
       </Cell>
     </CellGroup>
     <view class="flex items-center justify-center">
-      <button class="w-[176px] h-[47px] flex items-center justify-center bg-[#4545E5] border-none text-[white] rounded-3xl mt-[210px]">
+      <button class="w-[176px] h-[47px] flex items-center justify-center bg-[#4545E5] border-none text-[white] rounded-3xl mt-[210px]" @tap="confirm">
         下一步
       </button>
     </view>

+ 104 - 142
src/pages/feed-plan/index.vue

@@ -1,179 +1,141 @@
 <script setup lang="ts">
-import type { Pet } from '@/model/pet'
 import { getOwnPets } from '@/api/pet'
-import avatar from '@/static/image/pet-parameters/avatar.png'
-import addBtn from '@/static/image/pet-plan/add_btn.png'
-import card from '@/static/image/pet-plan/card.png'
-import editBtn from '@/static/image/pet-plan/edit.png'
-import userAddBtn from '@/static/image/pet-plan/user_add_btn.png'
-
-interface User {
-  name: string
-  image: string
-  label: string[]
-  isClick: boolean
-  plan: { image: string, name: string, weight: string, energy: string, label: string[] }[]
-}
+import { type Pet, PetType } from '@/model/pet'
 
 const pets = ref<Pet[]>([])
 async function fetchPets() {
   pets.value = await getOwnPets()
 }
 
-onMounted(fetchPets)
+const currentIndex = ref(0)
+const currentPet = computed(() => pets.value[currentIndex.value])
 
-const userList = ref<User[]>([])
-const clickUser = computed(() => {
-  return userList.value.filter(user => user.isClick === true)
+const petTags = computed(() => {
+  if (!currentPet.value)
+    return []
+  return [
+    { key: 'age', value: `${new Date().getFullYear() - new Date(currentPet.value.birthday).getFullYear()}岁` },
+    { key: 'type', value: currentPet.value.type === PetType.CAT ? '猫猫' : '狗狗' },
+    { key: 'weight', value: `${currentPet.value.weight}kg` },
+  ]
 })
-function resetUserClick() {
-  userList.value.forEach(i => (i.isClick = false))
-}
-function handleUser(index: number) {
-  resetUserClick()
-  userList.value[index].isClick = true
-}
-function handleAdd() {
-  uni.navigateTo({ url: '/pages/start-filing/index' })
-}
-function handleEdit() {
-  uni.navigateTo({ url: '/pages/feed-calculator/index' })
+
+function selectPet(index: number) {
+  currentIndex.value = index
 }
+
+onMounted(fetchPets)
 </script>
 
 <template>
-  <view class="flex flex-col bg-[#F5F6F7] overflow-y-auto h-screen">
-    <!--  喂养计划未注册  -->
-    <view v-if="pets.length === 0" class="w-[calc(100% - 32px)] min-h-[135px]  rounded-lg m-[16px] relative">
-      <image :src="card" class="w-full h-full absolute z-20" />
-      <view class="z-20 absolute w-full mt-[35px] flex flex-col">
-        <image :src="addBtn" class="w-[47px] h-[47px] mx-auto" @click="handleAdd" />
-        <text class="text-[12px] text-[#D9D9D9] mx-auto mt-[6px]" @click="handleAdd">
-          添加你的喂养计划
-        </text>
+  <view class="flex flex-col bg-[#F5F6F7] overflow-y-auto h-screen p-4 gap-4">
+    <view class="w-full flex gap-3 items-center h-[32px]">
+      <template v-if="pets.length">
+        <view
+          v-for="(item, index) in pets" :key="item.id" class="text-base font-medium relative flex justify-center transition-all"
+          :class="{
+            'text-primary text-xl font-semibold': index === currentIndex,
+          }"
+          @click="() => selectPet(index)"
+        >
+          {{ item.name }}
+          <view v-if="index === currentIndex" class="absolute w-[30%] h-[3px] bg-primary bottom-[-4px] rounded-full" />
+        </view>
+        <image src="@/static/icons/plus-circle.svg" class="w-[24px] h-[24px]" />
+      </template>
+      <view v-else class="text-primary text-xl relative flex justify-center font-semibold">
+        待添加
       </view>
-
-      <view class="w-[111px] h-[42px] bg-[black] absolute top-0 right-0 rounded-tr-xl z-10">
-        <text class="text-[#999] absolute right-5 top-2 text-[12px]">
-          无计划
-        </text>
+    </view>
+    <view v-if="currentPet" class="shadow-cell-group w-full px-4 py-8 bg-[white] rounded-3 flex items-center gap-4">
+      <image class="w-[64px] h-[64px] rounded-full" src="@/static/image/pet-default-avatar.png" />
+      <view class="flex-1">
+        <view class="font-semibold mb-1.5">
+          {{ currentPet.name }}
+        </view>
+        <view class="flex gap-2">
+          <view v-for="petTag in petTags" :key="petTag.key" class="px-2 py-0.5 bg-[#f3f3f3] rounded-0.75 text-[#999999]">
+            {{ petTag.value }}
+          </view>
+        </view>
       </view>
-      <view class="absolute right-0 top-0 w-[78px] h-[24px] z-20" @tap="handleAdd" />
+      <image class="w-[20px] h-[20px]" src="@/static/icons/edit.svg" />
     </view>
-    <!--  喂养计划已注册  -->
-    <view v-else>
-      <view class="w-[calc(100% - 32px)] m-[16px] flex items-center">
-        <view v-for="(item, index) in userList" :key="index" class="text-[16px] font-500 mr-[12px] flex flex-col items-center justify-center" :style="{ fontSize: item.isClick ? '20px' : '16px', color: item.isClick ? '#4545E5' : 'black', fontWeight: item.isClick ? '600' : '500' }" @click="handleUser(index)">
-          <text>{{ item.name }}</text>
-          <view v-if="item.isClick" class="w-[16px] h-[3px] bg-[#4545E5]" />
+    <view class="w-full  relative rounded-3 overflow-hidden shadow-cell-group bg-[#fff]">
+      <view class="w-[111px] h-[44px] bg-[#1a1a1a] absolute right-0 top-0">
+        <view class="text-[#999] text-xs absolute right-4 leading-[34px]">
+          编辑计划
         </view>
-        <image :src="userAddBtn" class="w-[24px] h-[24px]" @tap="handleAdd" />
       </view>
-
-      <view v-for="(item, index) in clickUser" :key="index" class="w-[calc(100% - 32px)] min-h-[129px] rounded-lg m-[16px] relative bg-[white] p-[16px] flex items-center">
-        <image :src="avatar" class="w-[64px] h-[64px] rounded-full" />
-        <view class="ml-[16px] min-h-[54px] flex-1">
-          <text class="font-600">
-            {{ item.name }}
-          </text>
-          <view class="flex gap-2 flex-wrap mt-[6px]">
-            <view v-for="(label, labelIndex) in item.label" :key="labelIndex" class="px-[8px] py-[4px]  rounded bg-[#f3f3f3] text-[#999] text-[12px]">
-              {{ label }}
+      <image class="w-[343px] h-[179px] absolute right-0 top-0 " src="@/static/image/shape-bg.png" />
+      <view class="pl-4 relative flex gap-1 h-[44px] items-center text-xs">
+        <view>喂养计划</view>
+        <view class="text-[#D9D9D9]">
+          FEEDING PLAN
+        </view>
+      </view>
+      <view class="py-4 pl-2 pr-8 w-full flex gap-3 relative items-center border-b-[1px] border-[#E7E7E7] last-of-type:border-0">
+        <image src="@/static/image/product-default.png" class="w-[80px] h-[80px]" />
+        <view class="flex-1 flex flex-col justify-between h-[80px] py-2">
+          <view class="font-semibold">
+            MINI主食包
+          </view>
+          <view class="flex gap-2">
+            <view class="px-2 py-0.5 bg-[#f3f3f3] rounded-0.75">
+              湿粮
+            </view>
+            <view class="px-2 py-0.5 bg-[#f3f3f3] rounded-0.75">
+              湿粮
             </view>
           </view>
         </view>
-        <image :src="editBtn" class="w-[20px] h-[20px]" />
-      </view>
-
-      <view class="w-[calc(100% - 32px)] min-h-[135px] rounded-lg bg-[white] mx-[16px] relative">
-        <image :src="card" class="w-full h-[130px] absolute z-20" />
-        <view class="absolute z-20 w-full rounded-lg">
-          <view class="ml-[12px] mt-[5px]">
-            <text class="text-[12px]">
-              喂养计划
-            </text>
-            <text class="text-[12px] text-[#D9D9D9]">
-              FEEDING PLAN
+        <view class="text-right">
+          <view class="text-xs font-semibold">
+            每日
+          </view>
+          <view class="text-xl font-bold mb-4">
+            500<text class="text-xs font-medium">
+              g
             </text>
           </view>
+          <view class="text-[9px] text-[#CDCDCD]">
+            1200Kcal
+          </view>
         </view>
-        <view class="absolute z-20 w-full rounded-lg bg-[white] top-[30px]">
-          <view v-for="(item, index) in clickUser[0].plan" :key="index" class="w-full mt-3 pl-[10px] pr-[20px] flex " :class="[clickUser[0].plan.length - 1 === index ? 'rounded-b-lg' : 'plan_card_bottom']">
-            <image :src="item.image" class="w-[80px] h-[80px]" />
-            <view class="ml-[12px] mt-[10px] flex-1">
-              <text class="font-600">
-                {{ item.name }}
-              </text>
-              <view class="flex gap-2 flex-wrap mt-[16px]">
-                <view v-for="(label, labelIndex) in item.label" :key="labelIndex" class="px-[8px] py-[4px] rounded bg-[#f3f3f3] text-[12px] ">
-                  {{ label }}
-                </view>
-              </view>
+      </view>
+      <view class="py-4 pl-2 pr-8 w-full flex gap-3 relative items-center  border-b-[1px] border-[#E7E7E7] last-of-type:border-0">
+        <image src="@/static/image/product-default.png" class="w-[80px] h-[80px]" />
+        <view class="flex-1 flex flex-col justify-between h-[80px] py-2">
+          <view class="font-semibold">
+            MINI主食包
+          </view>
+          <view class="flex gap-2">
+            <view class="px-2 py-0.5 bg-[#f3f3f3] rounded-0.75">
+              湿粮
             </view>
-            <view v-if="index === 0" class="flex flex-col mt-[10px]">
-              <view class="text-[12px] font-600 flex justify-end">
-                每日
-              </view>
-              <view class="flex items-end">
-                <text class="source_family font-700 text-[20px]">
-                  {{ item.weight }}
-                </text>
-                <text class="poppins_family font-500 text-[12px]">
-                  g
-                </text>
-              </view>
-              <view class="source_family text-[9px] text-[#CDCDCD] mt-[10px] flex justify-end">
-                {{ item.energy }}
-              </view>
-            </view>
-            <view v-else class="flex flex-col mt-[2px] relative items-center top-[8px] right-0">
-              <view class="absolute z-20 top-[-10px] right-0 flex items-center justify-center w-full">
-                <view class="w-[25px] h-[25px] rounded-full bg-[#4545e5] text-[10px] text-[white] flex items-center justify-center ">
-                  日
-                </view>
-              </view>
-
-              <view class="h-[52px] bg-[#D9D9D9] rounded-2xl  px-[4px]">
-                <view class="mt-[14px] flex items-end justify-center ">
-                  <text class="text-[16px] font-700">
-                    {{ item.weight }}
-                  </text>
-                  <text class="text-[12px] font-500">
-                    g
-                  </text>
-                </view>
-                <view class="text-[9px] text-[#7C7C7C] flex justify-center">
-                  {{ item.energy }}
-                </view>
-              </view>
+            <view class="px-2 py-0.5 bg-[#f3f3f3] rounded-0.75">
+              湿粮
             </view>
           </view>
         </view>
-        <view class="w-[111px] h-[42px] bg-[black] absolute top-0 right-0 rounded-tr-xl z-10">
-          <text class="text-[#999] absolute right-5 top-2 text-[12px]">
-            编辑计划
-          </text>
+        <view class="text-right">
+          <view class="text-xs font-semibold">
+            每日
+          </view>
+          <view class="text-xl font-bold mb-4">
+            500<text class="text-xs font-medium">
+              g
+            </text>
+          </view>
+          <view class="text-[9px] text-[#CDCDCD]">
+            1200Kcal
+          </view>
         </view>
-        <view class="absolute right-0 top-0 w-[78px] h-[24px] z-20" @tap="handleEdit" />
       </view>
     </view>
   </view>
 </template>
 
 <style scoped>
-.select_user_span{
-  color: #4545E5;
-}
-.user_span{
-  color:black;
-}
-.source_family{
-  font-family: Source Han Sans CN, sans-serif;
-}
-.poppins_family{
-  font-family: Poppins, sans-serif;
-}
-.plan_card_bottom{
-  border-bottom: 0.5px solid #EAEAEA;
-}
+
 </style>

+ 4 - 0
src/static/icons/edit.svg

@@ -0,0 +1,4 @@
+<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
+    <path d="M13.6024 2.17124L17.6566 6.22546L18.5405 5.34158L14.4863 1.28735L13.6024 2.17124Z" fill="#999999"/>
+    <path d="M2.94021 17.3291L7.45641 16.4259L16.5784 7.30385L12.5242 3.24963L3.40219 12.3716L2.49895 16.8878C2.44647 17.1502 2.67781 17.3816 2.94021 17.3291ZM12.5242 5.01739L14.8106 7.30385L6.84014 15.2744L3.98207 15.846L4.55369 12.9879L12.5242 5.01739Z" fill="#999999"/>
+</svg>

+ 4 - 0
src/static/icons/plus-circle.svg

@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+    <path d="M11.25 12.75V17.25H12.75V12.75H17.25V11.25H12.75V6.75H11.25V11.25H6.75V12.75H11.25Z" fill="#999999"/>
+    <path d="M12 22.5C17.799 22.5 22.5 17.799 22.5 12C22.5 6.20101 17.799 1.5 12 1.5C6.20101 1.5 1.5 6.20101 1.5 12C1.5 17.799 6.20101 22.5 12 22.5ZM12 21C7.02944 21 3 16.9706 3 12C3 7.02944 7.02944 3 12 3C16.9706 3 21 7.02944 21 12C21 16.9706 16.9706 21 12 21Z" fill="#999999"/>
+</svg>

+ 3 - 0
src/static/icons/shape-bg.svg

@@ -0,0 +1,3 @@
+<svg width="343" height="135" viewBox="0 0 343 135" fill="none" xmlns="http://www.w3.org/2000/svg">
+    <path d="M267.92 20.701L256.09 5.31098C253.52 1.96098 249.53 0.000976562 245.31 0.000976562H13.6C6.09 0.0109766 0 6.10098 0 13.611V179.011H343V39.611C343 32.101 336.91 26.011 329.4 26.011H278.69C274.47 26.011 270.49 24.051 267.91 20.701H267.92Z" fill="white"/>
+</svg>

二進制
src/static/image/pet-default-avatar.png


二進制
src/static/image/product-default.png


二進制
src/static/image/shape-bg.png


+ 8 - 0
src/stores/feeding-plan.ts

@@ -1,3 +1,4 @@
+import { createPet } from '@/api/pet'
 import {
   type CreateFeedingPlanRequest,
   type CreatePetRequest,
@@ -57,11 +58,18 @@ export const useFeedingPlanStore = defineStore('feeding-plan', () => {
     pet.value[key] = (value as CreatePetRequest[keyof CreatePetRequest])
   }
 
+  const persistentPet = async () => {
+    await createPet({
+      ...pet.value,
+    })
+  }
+
   return {
     pet,
     petTypeOptions,
     genderOptions,
     setPetValue,
+    persistentPet,
     petTags,
     feedingPlan,
     feedingGoalOptions,