2 Commits f99dca56ef ... 1dfddbf1da

Autor SHA1 Mensagem Data
  IlhamTahir 1dfddbf1da feat: 文章详情完成 há 11 meses atrás
  IlhamTahir b730f7966b feat: 喂养计划回显 há 11 meses atrás

+ 1 - 1
pet-manual/index.vue

@@ -1,6 +1,6 @@
 <script setup lang="ts">
 import carousalApi from '@/api/carousal'
-import CardList from '@/pages/pet-manual/components/CardList.vue'
+import CardList from '../src/components/CardList.vue'
 // const searchValue = ref('')
 import bg from '@/static/image/feed-plan/bg.png'
 import circle from '@/static/image/feed-plan/circle.png'

+ 6 - 0
src/api/article.ts

@@ -0,0 +1,6 @@
+import type { Article } from '@/model/pet-manual'
+import httpClient from '@/api/httpClient'
+
+export async function getArticle(id: string) {
+  return httpClient.get<Article>(`/articles/${id}`)
+}

+ 4 - 0
src/api/feeding-plan.ts

@@ -8,3 +8,7 @@ export async function createFeedingPlan(createFeedingPlanRequest: CreateFeedingP
 export async function getFeedingPlan(id: string) {
   return httpClient.get<FeedingPlan>(`/feeding-plans/${id}`)
 }
+
+export async function updateFeedingPlan(id: string, createFeedingPlanRequest: CreateFeedingPlanRequest) {
+  return httpClient.put<FeedingPlan>(`/feeding-plans/${id}`, createFeedingPlanRequest)
+}

+ 1 - 0
src/components.d.ts

@@ -10,6 +10,7 @@ declare module 'vue' {
     AddFeedingPlan: typeof import('./components/AddFeedingPlan.vue')['default']
     BButton: typeof import('./components/BButton.vue')['default']
     BSlider: typeof import('./components/BSlider.vue')['default']
+    CardList: typeof import('./components/CardList.vue')['default']
     Cell: typeof import('./components/Cell.vue')['default']
     CellGroup: typeof import('./components/CellGroup.vue')['default']
     FeedForm: typeof import('./components/FeedForm.vue')['default']

+ 7 - 0
src/pages/pet-manual/components/CardList.vue → src/components/CardList.vue

@@ -34,12 +34,19 @@ function handleLoadMore() {
   }
   emit('loadMore')
 }
+
+function goToArticle(id: string) {
+  uni.navigateTo({
+    url: `/pages/article/detail?id=${id}`,
+  })
+}
 </script>
 
 <template>
   <view class="flex flex-col gap-4 px-4">
     <view
       v-for="(item, index) in props.cardList" :key="index" class="bg-[white] rounded-md shadow-lg flex w-full h-[120px] overflow-hidden"
+      @tap="() => goToArticle(item.id)"
     >
       <image class="w-[120px] h-[120px] rounded-l-md shrink-0" :src="item?.imageUrl || titleBg" />
       <view class="p-4 flex flex-col">

+ 3 - 3
src/components/SectionCard.vue

@@ -15,13 +15,13 @@ function handleTap() {
 
 <template>
   <view class="w-full  relative rounded-3 overflow-hidden shadow-cell-group bg-[#fff]">
-    <view v-if="operationTitle" class="w-[111px] h-[44px] bg-[#1a1a1a] absolute right-0 top-0" @tap="handleTap">
+    <view v-if="operationTitle" class="w-[111px] h-[44px] bg-[#1a1a1a] absolute right-0 top-0">
       <view class="text-[#999] text-xs absolute right-4 leading-[34px]">
         {{ operationTitle }}
       </view>
     </view>
-    <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">
+    <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" @tap="handleTap">
       <view>{{ title }}</view>
       <view v-if="subtitle" class="text-[#D9D9D9]">
         {{ subtitle }}

+ 3 - 4
src/model/pet-manual.ts

@@ -1,4 +1,4 @@
-import type { Paging } from '@/model/base'
+import type { BaseModel, Paging } from '@/model/base'
 import type { CreatePetRequest } from '@/model/pet'
 
 export interface CreateCarousalsRequest {
@@ -20,14 +20,13 @@ export interface ArticleParams extends Paging {
   categoryId: string
 }
 
-export interface Article {
-  id: string
+export interface Article extends BaseModel {
   content: string
   status: string
   title: string
   description: string
-  updatedTime?: string
   imageUrl?: string
+  category: Category
 }
 
 export interface Recommends {

+ 1 - 1
src/model/pet.ts

@@ -64,5 +64,5 @@ export interface FeedingPlan extends BaseModel {
 
 export interface CreateFeedingPlanRequest extends Omit<FeedingPlan, keyof BaseModel> {
   petId: string
-  products: { id: string, dailyUsageWeight: number }[]
+  products: { id: string, dailyUsageWeight: number, percentage: number }[]
 }

+ 5 - 5
src/pages.json

@@ -7,6 +7,11 @@
   },
   "entryPagePath": "pages/home/index",
   "pages": [
+    {
+      "path": "pages/article/detail",
+      "type": "page",
+      "style": {}
+    },
     {
       "path": "pages/feed-plan/index",
       "type": "page",
@@ -86,11 +91,6 @@
       "path": "pages/userInfo/index",
       "type": "page",
       "style": {}
-    },
-    {
-      "path": "pages/pet-manual/components/CardList",
-      "type": "page",
-      "style": {}
     }
   ],
   "globalStyle": {

+ 46 - 0
src/pages/article/detail.vue

@@ -0,0 +1,46 @@
+<script setup lang="ts">
+import type { Article } from '@/model/pet-manual'
+import { getArticle } from '@/api/article'
+
+const article = ref<Article | null>()
+
+const id = ref('')
+
+onLoad((options) => {
+  id.value = options!.id as string
+})
+
+onMounted(() => {
+  fetchData()
+})
+
+async function fetchData() {
+  article.value = await getArticle(id.value)
+  await uni.setNavigationBarTitle({
+    title: article.value.category.name,
+  })
+}
+
+const fixedContent = computed(() => {
+  if (!article.value) {
+    return ''
+  }
+  return article.value.content.replace(/<img/g, '<img class="w-full"')
+})
+</script>
+
+<template>
+  <view v-if="article" class="w-full h-screen p-4 bg-[#F5F6F7]">
+    <view class="mb-2 text-xl font-semibold">
+      {{ article.title }}
+    </view>
+    <view class="flex text-xs text-[#999] mb-6">
+      <view>{{ article.createdTime }}</view>
+    </view>
+    <rich-text :nodes="fixedContent" />
+  </view>
+</template>
+
+<style scoped>
+
+</style>

+ 29 - 4
src/pages/feed-plan/index.vue

@@ -37,6 +37,31 @@ async function selectPet(index: number) {
   currentIndex.value = index
 }
 
+const feedingPlanStore = useFeedingPlanStore()
+
+async function handleAddFeedingPlan() {
+  feedingPlanStore.initPet(currentPet.value)
+  uni.navigateTo({
+    url: '/pages/feed-plan-calculator/index',
+  })
+}
+
+function handleAddPet() {
+  uni.navigateTo({
+    url: '/pages/start-filing/index',
+  })
+}
+
+async function handleEdit() {
+  if (!feedingPlan.value)
+    return
+  feedingPlanStore.initPet(currentPet.value)
+  feedingPlanStore.initFeedingPlan(feedingPlan.value)
+  uni.navigateTo({
+    url: '/pages/feed-plan-calculator/index',
+  })
+}
+
 onMounted(fetchPets)
 </script>
 
@@ -54,9 +79,9 @@ onMounted(fetchPets)
           {{ 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]" />
+        <image src="@/static/icons/plus-circle.svg" class="w-[24px] h-[24px]" @tap="handleAddPet" />
       </template>
-      <view v-else class="text-primary text-xl relative flex justify-center font-semibold">
+      <view v-else class="text-primary text-xl relative flex justify-center font-semibold" @tap="handleAddPet">
         待添加
       </view>
     </view>
@@ -74,11 +99,11 @@ onMounted(fetchPets)
       </view>
       <image class="w-[20px] h-[20px]" src="@/static/icons/edit.svg" />
     </view>
-    <SectionCard v-if="feedingPlan" title="喂养计划" subtitle="FEEDING PLAN" operation-title="编辑计划">
+    <SectionCard v-if="feedingPlan" title="喂养计划" subtitle="FEEDING PLAN" operation-title="编辑计划" :on-operation-click="handleEdit">
       <ProductReadonlyItem v-for="item in feedingPlan.feedingPlanProducts" :key="item.product.id" :data="item" />
     </SectionCard>
     <SectionCard v-else title="喂养计划" subtitle="FEEDING PLAN" operation-title="无计划">
-      <view class="w-full h-[120px] flex flex-col items-center justify-center gap-1.5">
+      <view class="w-full h-[120px] flex flex-col items-center justify-center gap-1.5" @tap="handleAddFeedingPlan">
         <image src="@/static/icons/plus-circle-2.svg" class="w-[47px] h-[47px]" />
         <view class="text-xs text-disabled">
           添加您的喂养计划

+ 1 - 1
src/pages/pet-manual/index.vue

@@ -2,7 +2,7 @@
 import type { Paging } from '@/model/base'
 import type { Article, ArticleParams, Category, CreateCarousalsRequest, Recommends } from '@/model/pet-manual'
 import carousalApi from '@/api/carousal'
-import CardList from '@/pages/pet-manual/components/CardList.vue'
+import CardList from '@/components/CardList.vue'
 import circle from '@/static/image/feed-plan/circle.png'
 import message from '@/static/image/feed-plan/message.png'
 import ToolApi from '@/utils'

+ 53 - 2
src/stores/feeding-plan.ts

@@ -1,11 +1,12 @@
 import type { FeedingPlanProduct } from '@/model/feeding-plan'
 import type { Product } from '@/model/product'
-import { createFeedingPlan } from '@/api/feeding-plan'
+import { createFeedingPlan, updateFeedingPlan } from '@/api/feeding-plan'
 import { createPet } from '@/api/pet'
 import {
   type CreateFeedingPlanRequest,
   type CreatePetRequest,
   FeedingGoal,
+  type FeedingPlan,
   Gender,
   type Pet,
   PetBodyType,
@@ -220,6 +221,8 @@ export const useFeedingPlanStore = defineStore('feeding-plan', () => {
     })
   }
 
+  const isEdit = ref(false)
+
   /**
    * 修改指定 productId 的 percentage
    * @param productId 产品的id
@@ -245,6 +248,7 @@ export const useFeedingPlanStore = defineStore('feeding-plan', () => {
       arrangeDailyConsumeWeight()
     }
   }
+  const savedFeedingPlan = ref<FeedingPlan | null>(null)
 
   const confirm = async () => {
     if (savedPet.value === null) {
@@ -256,9 +260,54 @@ export const useFeedingPlanStore = defineStore('feeding-plan', () => {
       return {
         id: item.product.id,
         dailyUsageWeight: item.dailyUsageWeight,
+        percentage: item.percentage,
       }
     })
-    await createFeedingPlan(feedingPlan.value)
+
+    if ((isEdit.value && savedFeedingPlan.value)) {
+      await updateFeedingPlan(savedFeedingPlan.value.id, feedingPlan.value)
+    }
+    else {
+      await createFeedingPlan(feedingPlan.value)
+    }
+  }
+
+  const initPet = (payload: Pet) => {
+    // @ts-expect-error @ts-ignore
+    pet.value = {
+      birthday: payload.birthday,
+      bodyType: payload.bodyType,
+      gender: payload.gender,
+      isActive: payload.isActive,
+      isLactation: payload.isLactation,
+      isPregnant: payload.isPregnant,
+      isSterilization: payload.isSterilization,
+      name: payload.name,
+      photo: payload.photo,
+      type: payload.type,
+      weight: payload.weight,
+    }
+    savedPet.value = payload
+    feedingPlan.value.targetWeight = calculateIdealWeight()
+    rer.value = calculateRER(pet.value.weight)
+  }
+
+  const initFeedingPlan = (payload: FeedingPlan) => {
+    // @ts-expect-error @ts-ignore
+    feedingPlan.value = {
+      feedingGoal: payload.feedingGoal,
+      targetWeight: payload.targetWeight,
+      petId: savedPet.value?.id || '',
+      products: payload.feedingPlanProducts.map((item) => {
+        return {
+          id: item.product.id,
+          dailyUsageWeight: item.dailyUsageWeight,
+        }
+      }),
+    }
+    savedFeedingPlan.value = payload
+    selectedProducts.value = payload.feedingPlanProducts
+    dailyCalories.value = Math.floor(rer.value * derRate.value)
   }
 
   return {
@@ -275,5 +324,7 @@ export const useFeedingPlanStore = defineStore('feeding-plan', () => {
     changePercentage,
     dailyCalories,
     confirm,
+    initPet,
+    initFeedingPlan,
   }
 })

+ 3 - 3
src/uni-pages.d.ts

@@ -4,7 +4,8 @@
 // Generated by vite-plugin-uni-pages
 
 interface NavigateToOptions {
-  url: "/pages/feed-plan/index" |
+  url: "/pages/article/detail" |
+       "/pages/feed-plan/index" |
        "/pages/feed-plan-calculator/choose-product" |
        "/pages/feed-plan-calculator/confirm" |
        "/pages/feed-plan-calculator/index" |
@@ -14,8 +15,7 @@ interface NavigateToOptions {
        "/pages/pet-manual/index" |
        "/pages/setting/index" |
        "/pages/start-filing/index" |
-       "/pages/userInfo/index" |
-       "/pages/pet-manual/components/CardList";
+       "/pages/userInfo/index";
 }
 interface RedirectToOptions extends NavigateToOptions {}