ソースを参照

fix(web): 补交健康/测评方案管理页面,修复部署构建找不到路由组件

Co-authored-by: Cursor <cursoragent@cursor.com>
jiapu 3 週間 前
コミット
de6b51b3aa

+ 16 - 0
cfc-web/src/views/admin/AssessmentPlanManage.vue

@@ -0,0 +1,16 @@
+<template>
+  <div class="assessment-plan-manage admin-page">
+    <el-card>
+      <div slot="header" class="admin-page-header">
+        <span class="admin-page-title">测评方案管理</span>
+      </div>
+      <el-empty description="功能开发中,敬请期待" />
+    </el-card>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'AssessmentPlanManage'
+}
+</script>

+ 252 - 0
cfc-web/src/views/admin/HealthPlanManage.vue

@@ -0,0 +1,252 @@
+<template>
+  <div class="health-plan-manage admin-page">
+    <el-card>
+      <div slot="header" class="admin-page-header">
+        <span class="admin-page-title">健康方案管理</span>
+        <div class="flex items-center gap-sm">
+          <el-input
+            v-model="keyword"
+            placeholder="搜索成员/目标"
+            prefix-icon="el-icon-search"
+            clearable
+            style="width:200px"
+            @keyup.enter.native="handleSearch"
+            @clear="handleSearch"
+          />
+          <el-select v-model="statusFilter" placeholder="状态" clearable style="width:130px" @change="handleSearch">
+            <el-option label="草稿" value="draft" />
+            <el-option label="待审核" value="pending_review" />
+            <el-option label="已发布" value="published" />
+            <el-option label="已驳回" value="rejected" />
+          </el-select>
+          <el-input
+            v-model="familyIdFilter"
+            placeholder="家庭ID"
+            clearable
+            style="width:120px"
+            @keyup.enter.native="handleSearch"
+            @clear="handleSearch"
+          />
+          <el-button type="primary" size="mini" @click="handleSearch">查询</el-button>
+        </div>
+      </div>
+
+      <div class="table-scroll-wrap">
+        <el-table :data="list" v-loading="loading" border stripe :max-height="tableHeight">
+          <el-table-column prop="id" label="ID" width="70" />
+          <el-table-column label="家庭" min-width="120" show-overflow-tooltip>
+            <template slot-scope="{ row }">
+              {{ row.familyName || ('家庭' + (row.familyId || '-')) }}
+            </template>
+          </el-table-column>
+          <el-table-column label="成员" width="140" show-overflow-tooltip>
+            <template slot-scope="{ row }">{{ row.memberName || row.memberIds || '-' }}</template>
+          </el-table-column>
+          <el-table-column label="目标" min-width="180" show-overflow-tooltip>
+            <template slot-scope="{ row }">{{ row.goal || '-' }}</template>
+          </el-table-column>
+          <el-table-column label="维度" width="140">
+            <template slot-scope="{ row }">
+              <el-tag
+                v-for="(d, i) in splitDimensions(row.dimensions)"
+                :key="i"
+                size="mini"
+                style="margin-right:4px"
+              >{{ d }}</el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column label="状态" width="100">
+            <template slot-scope="{ row }">
+              <el-tag :type="statusType(row.status)" size="mini">{{ statusLabel(row.status) }}</el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column label="创建时间" width="170">
+            <template slot-scope="{ row }">{{ formatTime(row.createdAt) }}</template>
+          </el-table-column>
+          <el-table-column label="操作" width="100" fixed="right">
+            <template slot-scope="{ row }">
+              <el-button size="mini" type="text" @click="showDetail(row)">详情</el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+      </div>
+
+      <el-pagination
+        style="margin-top:16px;text-align:right"
+        layout="total, prev, pager, next, sizes"
+        :current-page.sync="page"
+        :page-size.sync="size"
+        :page-sizes="[10, 20, 50]"
+        :total="total"
+        @current-change="loadList"
+        @size-change="handleSizeChange"
+      />
+    </el-card>
+
+    <el-dialog title="健康方案详情" :visible.sync="detailVisible" width="720px" destroy-on-close>
+      <div v-loading="detailLoading" v-if="currentPlan">
+        <el-descriptions :column="2" border size="small">
+          <el-descriptions-item label="方案ID">{{ currentPlan.id }}</el-descriptions-item>
+          <el-descriptions-item label="状态">
+            <el-tag :type="statusType(currentPlan.status)" size="mini">{{ statusLabel(currentPlan.status) }}</el-tag>
+          </el-descriptions-item>
+          <el-descriptions-item label="家庭">
+            {{ currentPlan.familyName || ('家庭' + (currentPlan.familyId || '-')) }}
+            <span v-if="currentPlan.familyId" style="color:#999;margin-left:6px">(#{{ currentPlan.familyId }})</span>
+          </el-descriptions-item>
+          <el-descriptions-item label="成员">{{ currentPlan.memberName || currentPlan.memberIds || '-' }}</el-descriptions-item>
+          <el-descriptions-item label="维度" :span="2">{{ currentPlan.dimensions || '-' }}</el-descriptions-item>
+          <el-descriptions-item label="目标" :span="2">{{ currentPlan.goal || '-' }}</el-descriptions-item>
+          <el-descriptions-item label="创建时间">{{ formatTime(currentPlan.createdAt) }}</el-descriptions-item>
+          <el-descriptions-item label="更新时间">{{ formatTime(currentPlan.updatedAt) }}</el-descriptions-item>
+          <el-descriptions-item v-if="currentPlan.reviewComment" label="审核意见" :span="2">
+            {{ currentPlan.reviewComment }}
+          </el-descriptions-item>
+        </el-descriptions>
+
+        <div class="detail-section-title">方案内容</div>
+        <pre class="plan-content">{{ currentPlan.planContent || '暂无内容' }}</pre>
+
+        <div v-if="currentPlan.tasks && currentPlan.tasks.length" class="detail-section-title">
+          分解任务({{ currentPlan.tasks.length }})
+        </div>
+        <el-table
+          v-if="currentPlan.tasks && currentPlan.tasks.length"
+          :data="currentPlan.tasks"
+          border
+          size="mini"
+          style="margin-top:8px"
+        >
+          <el-table-column prop="id" label="ID" width="70" />
+          <el-table-column prop="title" label="任务" min-width="180" show-overflow-tooltip />
+          <el-table-column prop="status" label="状态" width="100" />
+          <el-table-column label="截止时间" width="160">
+            <template slot-scope="{ row }">{{ formatTime(row.deadline) }}</template>
+          </el-table-column>
+        </el-table>
+      </div>
+      <div slot="footer">
+        <el-button @click="detailVisible = false">关闭</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { adminListHealthPlans, adminGetHealthPlanDetail } from '@/api/healthPlan'
+
+export default {
+  name: 'HealthPlanManage',
+  computed: {
+    tableHeight() {
+      return window.innerHeight - 320
+    }
+  },
+  data() {
+    return {
+      loading: false,
+      detailLoading: false,
+      list: [],
+      total: 0,
+      page: 1,
+      size: 20,
+      keyword: '',
+      statusFilter: '',
+      familyIdFilter: '',
+      detailVisible: false,
+      currentPlan: null
+    }
+  },
+  created() {
+    this.loadList()
+  },
+  methods: {
+    formatTime(t) {
+      return t ? String(t).replace('T', ' ').substring(0, 19) : '-'
+    },
+    splitDimensions(dims) {
+      if (!dims) return []
+      return String(dims).split(',').map(function (s) { return s.trim() }).filter(Boolean)
+    },
+    statusType(s) {
+      var map = { pending_review: 'warning', draft: 'info', published: 'success', rejected: 'danger' }
+      return map[s] || 'info'
+    },
+    statusLabel(s) {
+      var map = { pending_review: '待审核', draft: '草稿', published: '已发布', rejected: '已驳回' }
+      return map[s] || s || '-'
+    },
+    handleSearch() {
+      this.page = 1
+      this.loadList()
+    },
+    handleSizeChange() {
+      this.page = 1
+      this.loadList()
+    },
+    async loadList() {
+      this.loading = true
+      try {
+        var familyId = this.familyIdFilter ? Number(this.familyIdFilter) : null
+        if (familyId !== null && isNaN(familyId)) familyId = null
+        var res = await adminListHealthPlans({
+          page: this.page,
+          size: this.size,
+          keyword: this.keyword || null,
+          status: this.statusFilter || null,
+          familyId: familyId
+        })
+        if (res.code === 200) {
+          var data = res.data || {}
+          this.list = data.records || []
+          this.total = data.total || 0
+        } else {
+          this.$message.error(res.message || '加载失败')
+        }
+      } catch (e) {
+        this.$message.error('加载失败')
+      } finally {
+        this.loading = false
+      }
+    },
+    async showDetail(row) {
+      this.detailVisible = true
+      this.currentPlan = row
+      this.detailLoading = true
+      try {
+        var res = await adminGetHealthPlanDetail({ planId: row.id })
+        if (res.code === 200) {
+          this.currentPlan = res.data
+        } else {
+          this.$message.error(res.message || '加载详情失败')
+        }
+      } catch (e) {
+        this.$message.error('加载详情失败')
+      } finally {
+        this.detailLoading = false
+      }
+    }
+  }
+}
+</script>
+
+<style scoped>
+.detail-section-title {
+  margin: 16px 0 8px;
+  font-weight: 600;
+  color: #303133;
+}
+.plan-content {
+  white-space: pre-wrap;
+  word-break: break-word;
+  background: #f5f7fa;
+  padding: 12px;
+  border-radius: 6px;
+  max-height: 360px;
+  overflow: auto;
+  margin: 0;
+  font-size: 13px;
+  line-height: 1.6;
+  color: #303133;
+}
+</style>