Przeglądaj źródła

feat: add nutritionist/teacher/admin views

Add Activities.vue and ActivityReview.vue for activity_admin role. Add teacher views (FamilyList, GrowthPlans, GrowthRecords) and nutritionist views (HealthReports, HealthIndicators, HealthCheckins).

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Sisyphus 4 miesięcy temu
rodzic
commit
927406eac0

+ 51 - 0
cfc-web/src/views/admin/Activities.vue

@@ -0,0 +1,51 @@
+<template>
+  <div class="activities">
+    <el-card>
+      <div slot="header">
+        <span>活动管理</span>
+        <el-button type="primary" size="mini" style="float:right" @click="showCreate = true">创建活动</el-button>
+      </div>
+      <el-table :data="activities" v-loading="loading" border stripe>
+        <el-table-column prop="id" label="ID" width="80" />
+        <el-table-column prop="title" label="活动标题" />
+        <el-table-column prop="status" label="状态" width="100" />
+        <el-table-column prop="startTime" label="开始时间" width="180" />
+        <el-table-column prop="endTime" label="结束时间" width="180" />
+        <el-table-column label="操作" width="150">
+          <template slot-scope="{ row }">
+            <el-button size="mini" @click="editActivity(row)">编辑</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+    </el-card>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'Activities',
+  data() { return { loading: false, activities: [], showCreate: false } },
+  created() { this.loadActivities() },
+  methods: {
+    async loadActivities() {
+      this.loading = true
+      try {
+        const baseURL = process.env.VUE_APP_BASE_API || ''
+        const token = localStorage.getItem('token')
+        const axios = this.$axios || (await import('axios')).default
+        const res = await axios.post(baseURL + '/api/activity/list', {}, {
+          headers: { Authorization: 'Bearer ' + token }
+        })
+        this.activities = res.data?.data || []
+      } catch (e) {
+        this.$message?.error?.('加载活动列表失败')
+      } finally {
+        this.loading = false
+      }
+    },
+    editActivity(row) {
+      this.$message.info('活动编辑功能待实现')
+    }
+  }
+}
+</script>

+ 47 - 0
cfc-web/src/views/admin/ActivityReview.vue

@@ -0,0 +1,47 @@
+<template>
+  <div class="activity-review">
+    <el-card>
+      <div slot="header"><span>活动审核</span></div>
+      <el-table :data="pendingActivities" v-loading="loading" border stripe>
+        <el-table-column prop="id" label="ID" width="80" />
+        <el-table-column prop="title" label="活动标题" />
+        <el-table-column prop="applicant" label="申请人" width="120" />
+        <el-table-column prop="createdAt" label="申请时间" width="180" />
+        <el-table-column label="操作" width="200">
+          <template slot-scope="{ row }">
+            <el-button size="mini" type="success" @click="approve(row)">通过</el-button>
+            <el-button size="mini" type="danger" @click="reject(row)">驳回</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+    </el-card>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'ActivityReview',
+  data() { return { loading: false, pendingActivities: [] } },
+  created() { this.loadPending() },
+  methods: {
+    async loadPending() {
+      this.loading = true
+      try {
+        const baseURL = process.env.VUE_APP_BASE_API || ''
+        const token = localStorage.getItem('token')
+        const axios = this.$axios || (await import('axios')).default
+        const res = await axios.post(baseURL + '/api/activity/list', { status: 'pending' }, {
+          headers: { Authorization: 'Bearer ' + token }
+        })
+        this.pendingActivities = res.data?.data || []
+      } catch (e) {
+        this.$message?.error?.('加载待审核活动失败')
+      } finally {
+        this.loading = false
+      }
+    },
+    approve(row) { this.$message.info('审核通过功能待实现') },
+    reject(row) { this.$message.info('驳回功能待实现') }
+  }
+}
+</script>

+ 40 - 0
cfc-web/src/views/nutritionist/HealthCheckins.vue

@@ -0,0 +1,40 @@
+<template>
+  <div class="health-checkins">
+    <el-card>
+      <div slot="header"><span>健康打卡</span></div>
+      <el-table :data="checkins" v-loading="loading" border stripe>
+        <el-table-column prop="id" label="ID" width="80" />
+        <el-table-column prop="childName" label="孩子" width="120" />
+        <el-table-column prop="checkinType" label="打卡类型" width="120" />
+        <el-table-column prop="note" label="备注" />
+        <el-table-column prop="createdAt" label="打卡时间" width="180" />
+      </el-table>
+    </el-card>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'HealthCheckins',
+  data() { return { loading: false, checkins: [] } },
+  created() { this.loadCheckins() },
+  methods: {
+    async loadCheckins() {
+      this.loading = true
+      try {
+        const baseURL = process.env.VUE_APP_BASE_API || ''
+        const token = localStorage.getItem('token')
+        const axios = this.$axios || (await import('axios')).default
+        const res = await axios.post(baseURL + '/api/health/checkin/list', {}, {
+          headers: { Authorization: 'Bearer ' + token }
+        })
+        this.checkins = res.data?.data || []
+      } catch (e) {
+        this.$message?.error?.('加载健康打卡失败')
+      } finally {
+        this.loading = false
+      }
+    }
+  }
+}
+</script>

+ 40 - 0
cfc-web/src/views/nutritionist/HealthIndicators.vue

@@ -0,0 +1,40 @@
+<template>
+  <div class="health-indicators">
+    <el-card>
+      <div slot="header"><span>健康指标</span></div>
+      <el-table :data="indicators" v-loading="loading" border stripe>
+        <el-table-column prop="id" label="ID" width="80" />
+        <el-table-column prop="childName" label="孩子" width="120" />
+        <el-table-column prop="indicatorName" label="指标名称" width="120" />
+        <el-table-column prop="value" label="数值" width="100" />
+        <el-table-column prop="recordedAt" label="记录时间" width="180" />
+      </el-table>
+    </el-card>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'HealthIndicators',
+  data() { return { loading: false, indicators: [] } },
+  created() { this.loadIndicators() },
+  methods: {
+    async loadIndicators() {
+      this.loading = true
+      try {
+        const baseURL = process.env.VUE_APP_BASE_API || ''
+        const token = localStorage.getItem('token')
+        const axios = this.$axios || (await import('axios')).default
+        const res = await axios.post(baseURL + '/api/health/indicator/list', {}, {
+          headers: { Authorization: 'Bearer ' + token }
+        })
+        this.indicators = res.data?.data || []
+      } catch (e) {
+        this.$message?.error?.('加载健康指标失败')
+      } finally {
+        this.loading = false
+      }
+    }
+  }
+}
+</script>

+ 40 - 0
cfc-web/src/views/nutritionist/HealthReports.vue

@@ -0,0 +1,40 @@
+<template>
+  <div class="health-reports">
+    <el-card>
+      <div slot="header"><span>健康报告</span></div>
+      <el-table :data="reports" v-loading="loading" border stripe>
+        <el-table-column prop="id" label="ID" width="80" />
+        <el-table-column prop="childName" label="孩子" width="120" />
+        <el-table-column prop="reportType" label="报告类型" width="120" />
+        <el-table-column prop="summary" label="摘要" />
+        <el-table-column prop="createdAt" label="报告日期" width="180" />
+      </el-table>
+    </el-card>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'HealthReports',
+  data() { return { loading: false, reports: [] } },
+  created() { this.loadReports() },
+  methods: {
+    async loadReports() {
+      this.loading = true
+      try {
+        const baseURL = process.env.VUE_APP_BASE_API || ''
+        const token = localStorage.getItem('token')
+        const axios = this.$axios || (await import('axios')).default
+        const res = await axios.post(baseURL + '/api/health/report/list', {}, {
+          headers: { Authorization: 'Bearer ' + token }
+        })
+        this.reports = res.data?.data || []
+      } catch (e) {
+        this.$message?.error?.('加载健康报告失败')
+      } finally {
+        this.loading = false
+      }
+    }
+  }
+}
+</script>

+ 51 - 0
cfc-web/src/views/teacher/FamilyList.vue

@@ -0,0 +1,51 @@
+<template>
+  <div class="family-list">
+    <el-card>
+      <div slot="header">
+        <span>我的家庭</span>
+      </div>
+      <el-table :data="families" v-loading="loading" border stripe>
+        <el-table-column prop="id" label="ID" width="80" />
+        <el-table-column prop="familyName" label="家庭名称" />
+        <el-table-column prop="memberCount" label="成员数" width="100" />
+        <el-table-column prop="createdAt" label="绑定时间" width="180" />
+        <el-table-column label="操作" width="200">
+          <template slot-scope="{ row }">
+            <el-button size="mini" type="primary" @click="viewFamily(row)">查看详情</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+    </el-card>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'FamilyList',
+  data() {
+    return { loading: false, families: [] }
+  },
+  created() { this.loadFamilies() },
+  methods: {
+    async loadFamilies() {
+      this.loading = true
+      try {
+        const token = localStorage.getItem('token')
+        const baseURL = process.env.VUE_APP_BASE_API || ''
+        const axios = this.$axios || (await import('axios')).default
+        const res = await axios.post(baseURL + '/api/guide/family/list', {}, {
+          headers: { Authorization: 'Bearer ' + token }
+        })
+        this.families = res.data?.data || []
+      } catch (e) {
+        this.$message?.error?.('加载家庭列表失败')
+      } finally {
+        this.loading = false
+      }
+    },
+    viewFamily(row) {
+      this.$router.push('/my-families/' + row.id)
+    }
+  }
+}
+</script>

+ 40 - 0
cfc-web/src/views/teacher/GrowthPlans.vue

@@ -0,0 +1,40 @@
+<template>
+  <div class="growth-plans">
+    <el-card>
+      <div slot="header"><span>成长计划</span></div>
+      <el-table :data="plans" v-loading="loading" border stripe>
+        <el-table-column prop="id" label="ID" width="80" />
+        <el-table-column prop="childName" label="孩子" width="120" />
+        <el-table-column prop="title" label="计划标题" />
+        <el-table-column prop="status" label="状态" width="100" />
+        <el-table-column prop="createdAt" label="创建时间" width="180" />
+      </el-table>
+    </el-card>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'GrowthPlans',
+  data() { return { loading: false, plans: [] } },
+  created() { this.loadPlans() },
+  methods: {
+    async loadPlans() {
+      this.loading = true
+      try {
+        const baseURL = process.env.VUE_APP_BASE_API || ''
+        const token = localStorage.getItem('token')
+        const axios = this.$axios || (await import('axios')).default
+        const res = await axios.post(baseURL + '/api/growth/plan/list', {}, {
+          headers: { Authorization: 'Bearer ' + token }
+        })
+        this.plans = res.data?.data || []
+      } catch (e) {
+        this.$message?.error?.('加载成长计划失败')
+      } finally {
+        this.loading = false
+      }
+    }
+  }
+}
+</script>

+ 40 - 0
cfc-web/src/views/teacher/GrowthRecords.vue

@@ -0,0 +1,40 @@
+<template>
+  <div class="growth-records">
+    <el-card>
+      <div slot="header"><span>成长记录</span></div>
+      <el-table :data="records" v-loading="loading" border stripe>
+        <el-table-column prop="id" label="ID" width="80" />
+        <el-table-column prop="childName" label="孩子" width="120" />
+        <el-table-column prop="recordType" label="记录类型" width="120" />
+        <el-table-column prop="content" label="内容" />
+        <el-table-column prop="createdAt" label="记录时间" width="180" />
+      </el-table>
+    </el-card>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'GrowthRecords',
+  data() { return { loading: false, records: [] } },
+  created() { this.loadRecords() },
+  methods: {
+    async loadRecords() {
+      this.loading = true
+      try {
+        const baseURL = process.env.VUE_APP_BASE_API || ''
+        const token = localStorage.getItem('token')
+        const axios = this.$axios || (await import('axios')).default
+        const res = await axios.post(baseURL + '/api/growth/record/list', {}, {
+          headers: { Authorization: 'Bearer ' + token }
+        })
+        this.records = res.data?.data || []
+      } catch (e) {
+        this.$message?.error?.('加载成长记录失败')
+      } finally {
+        this.loading = false
+      }
+    }
+  }
+}
+</script>