Преглед изворни кода

feat(admin): 健康知识库管理后台 CRUD

新增 Web 管理端的健康知识库条目 CRUD 能力:
- HealthKnowledgeBaseService: listAll/save/updateById/removeById
- HealthKnowledgeBaseController: /list /add /update /delete
- cfc-web/views/admin/health-knowledge.vue: 列表+筛选+新增/编辑/删除/导入
- router: 注册 /health-knowledge,Layout 侧边栏 知识中心 下新增菜单
iwt пре 2 месеци
родитељ
комит
25686fe012

+ 28 - 0
cfc-backend/src/main/java/com/etotem/cfc/controller/HealthKnowledgeBaseController.java

@@ -52,6 +52,34 @@ public class HealthKnowledgeBaseController {
         return Result.success(result);
         return Result.success(result);
     }
     }
 
 
+    @Operation(summary = "列表查询知识库")
+    @PostMapping("/list")
+    public Result<List<HealthKnowledgeBase>> list() {
+        return Result.success(healthKnowledgeBaseService.listAll());
+    }
+
+    @Operation(summary = "新增知识库条目")
+    @PostMapping("/add")
+    public Result add(@RequestBody HealthKnowledgeBase entity) {
+        healthKnowledgeBaseService.save(entity);
+        return Result.success();
+    }
+
+    @Operation(summary = "更新知识库条目")
+    @PostMapping("/update")
+    public Result update(@RequestBody HealthKnowledgeBase entity) {
+        healthKnowledgeBaseService.updateById(entity);
+        return Result.success();
+    }
+
+    @Operation(summary = "删除知识库条目")
+    @PostMapping("/delete")
+    public Result delete(@RequestBody Map<String, Object> params) {
+        Long id = Long.valueOf(params.get("id").toString());
+        healthKnowledgeBaseService.removeById(id);
+        return Result.success();
+    }
+
     @Operation(summary = "从参考JSON导入知识库数据")
     @Operation(summary = "从参考JSON导入知识库数据")
     @PostMapping("/import")
     @PostMapping("/import")
     public Result<String> importKnowledgeBase() {
     public Result<String> importKnowledgeBase() {

+ 20 - 0
cfc-backend/src/main/java/com/etotem/cfc/service/HealthKnowledgeBaseService.java

@@ -56,4 +56,24 @@ public class HealthKnowledgeBaseService {
     public void insert(HealthKnowledgeBase entry) {
     public void insert(HealthKnowledgeBase entry) {
         healthKnowledgeBaseMapper.insert(entry);
         healthKnowledgeBaseMapper.insert(entry);
     }
     }
+
+    public List<HealthKnowledgeBase> listAll() {
+        return healthKnowledgeBaseMapper.selectList(null);
+    }
+
+    public void save(HealthKnowledgeBase entry) {
+        if (entry.getId() != null) {
+            healthKnowledgeBaseMapper.updateById(entry);
+        } else {
+            healthKnowledgeBaseMapper.insert(entry);
+        }
+    }
+
+    public void updateById(HealthKnowledgeBase entry) {
+        healthKnowledgeBaseMapper.updateById(entry);
+    }
+
+    public void removeById(Long id) {
+        healthKnowledgeBaseMapper.deleteById(id);
+    }
 }
 }

+ 6 - 0
cfc-web/src/router/index.js

@@ -520,6 +520,12 @@ const routes = [
         component: () => import('@/views/admin/knowledge'),
         component: () => import('@/views/admin/knowledge'),
         meta: { title: '知识库管理', perm: 'system:config' }
         meta: { title: '知识库管理', perm: 'system:config' }
       },
       },
+      {
+        path: 'health-knowledge',
+        name: 'HealthKnowledge',
+        component: () => import('@/views/admin/health-knowledge'),
+        meta: { title: '健康知识库管理', perm: 'system:config' }
+      },
       // ========== 健康维度配置 ==========
       // ========== 健康维度配置 ==========
       {
       {
         path: 'health-norm-config',
         path: 'health-norm-config',

+ 1 - 0
cfc-web/src/views/Layout.vue

@@ -203,6 +203,7 @@ export default {
             { path: '/article-manage', label: '知识管理', icon: 'el-icon-document', perm: 'articles:manage' },
             { path: '/article-manage', label: '知识管理', icon: 'el-icon-document', perm: 'articles:manage' },
             { path: '/knowledge-tags', label: '知识标签', icon: 'el-icon-price-tag', perm: 'articles:categories' },
             { path: '/knowledge-tags', label: '知识标签', icon: 'el-icon-price-tag', perm: 'articles:categories' },
             { path: '/knowledge-base', label: '知识库', icon: 'el-icon-reading', perm: 'system:config' },
             { path: '/knowledge-base', label: '知识库', icon: 'el-icon-reading', perm: 'system:config' },
+            { path: '/health-knowledge', label: '健康知识库', icon: 'el-icon-first-aid-kit', perm: 'system:config' },
             { path: '/activities', label: '活动列表', icon: 'el-icon-date', perm: 'activity:list' },
             { path: '/activities', label: '活动列表', icon: 'el-icon-date', perm: 'activity:list' },
             { path: '/activity-review', label: '活动审核', icon: 'el-icon-document-checked', perm: 'activity:review' },
             { path: '/activity-review', label: '活动审核', icon: 'el-icon-document-checked', perm: 'activity:review' },
             { path: '/activity-registration-review', label: '活动报名审核', icon: 'el-icon-document-checked', perm: 'activity:review' },
             { path: '/activity-registration-review', label: '活动报名审核', icon: 'el-icon-document-checked', perm: 'activity:review' },

+ 148 - 0
cfc-web/src/views/admin/health-knowledge.vue

@@ -0,0 +1,148 @@
+<template>
+  <div class="knowledge-page">
+    <el-card>
+      <div slot="header" class="clearfix">
+        <span>健康知识库管理</span>
+        <div style="float:right; display:flex; gap:10px;">
+          <el-button type="warning" size="small" @click="importData">导入参考数据</el-button>
+          <el-button type="primary" size="small" @click="showAddDialog">新增条目</el-button>
+        </div>
+      </div>
+
+      <!-- 搜索 -->
+      <el-form :model="filters" inline size="small" style="margin-bottom:16px;">
+        <el-form-item label="名称">
+          <el-input v-model="filters.name" placeholder="搜索指标名称" clearable @input="fetchList" />
+        </el-form-item>
+        <el-form-item label="分类">
+          <el-select v-model="filters.category" placeholder="全部分类" clearable @change="fetchList" style="width:160px;">
+            <el-option v-for="c in categories" :key="c" :label="c" :value="c" />
+          </el-select>
+        </el-form-item>
+      </el-form>
+
+      <el-table :data="list" border stripe v-loading="loading" empty-text="暂无数据" style="width:100%">
+        <el-table-column prop="id" label="ID" width="60" />
+        <el-table-column prop="itemName" label="指标名称" min-width="140" />
+        <el-table-column prop="category" label="分类" width="140" />
+        <el-table-column prop="normalRange" label="正常范围" width="140" />
+        <el-table-column prop="description" label="说明" min-width="260" show-overflow-tooltip />
+        <el-table-column prop="source" label="来源" width="120" show-overflow-tooltip />
+        <el-table-column label="操作" width="130" fixed="right">
+          <template slot-scope="{ row }">
+            <el-button type="text" @click="editRow(row)">编辑</el-button>
+            <el-button type="text" style="color:#f56c6c" @click="deleteRow(row)">删除</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+    </el-card>
+
+    <el-dialog :visible.sync="dialog.visible" :title="dialog.isEdit ? '编辑知识库条目' : '新增知识库条目'" width="560px" :close-on-click-modal="false">
+      <el-form :model="dialog.form" label-width="100px" size="small">
+        <el-form-item label="指标名称" required>
+          <el-input v-model="dialog.form.itemName" placeholder="如:维生素D" />
+        </el-form-item>
+        <el-form-item label="分类" required>
+          <el-select v-model="dialog.form.category" placeholder="请选择分类" style="width:100%">
+            <el-option v-for="c in categories" :key="c" :label="c" :value="c" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="正常范围">
+          <el-input v-model="dialog.form.normalRange" placeholder="如:30-100 ng/mL" />
+        </el-form-item>
+        <el-form-item label="说明">
+          <el-input type="textarea" :rows="4" v-model="dialog.form.description" placeholder="指标说明..." />
+        </el-form-item>
+        <el-form-item label="来源">
+          <el-input v-model="dialog.form.source" placeholder="数据来源" />
+        </el-form-item>
+      </el-form>
+      <div slot="footer">
+        <el-button @click="dialog.visible = false" size="small">取消</el-button>
+        <el-button type="primary" @click="saveDialog" size="small" :loading="dialog.saving">保存</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+export default {
+  data() {
+    return {
+      loading: false,
+      list: [],
+      filters: { name: '', category: '' },
+      categories: ['维生素', '氨基酸', '微量元素', '短链脂肪酸', '神经递质与激素', '肠道屏障与代谢物', '抗生素风险评估'],
+      dialog: { visible: false, isEdit: false, saving: false, form: {} }
+    }
+  },
+  mounted() { this.fetchList() },
+  methods: {
+    fetchList() {
+      this.loading = true
+      var params = {}
+      if (this.filters.name) params.name = this.filters.name
+      if (this.filters.category) params.category = this.filters.category
+      this.$axios.post('/api/health/knowledge/list', params).then(res => {
+        var data = res.data || {}
+        if (data.code === 200) {
+          var rows = data.data || []
+          if (this.filters.name) {
+            rows = rows.filter(function(r) { return r.itemName && r.itemName.indexOf(this.filters.name) >= 0 }.bind(this))
+          }
+          if (this.filters.category) {
+            rows = rows.filter(function(r) { return r.category === this.filters.category }.bind(this))
+          }
+          this.list = rows
+        }
+      }).finally(() => { this.loading = false })
+    },
+    showAddDialog() {
+      this.dialog = { visible: true, isEdit: false, saving: false, form: { itemName: '', category: '', normalRange: '', description: '', source: '' } }
+    },
+    editRow(row) {
+      this.dialog = { visible: true, isEdit: true, saving: false, form: { id: row.id, itemName: row.itemName, category: row.category, normalRange: row.normalRange, description: row.description, source: row.source } }
+    },
+    saveDialog() {
+      if (!this.dialog.form.itemName || !this.dialog.form.category) {
+        this.$message.warning('指标名称和分类不能为空'); return
+      }
+      this.dialog.saving = true
+      var url = this.dialog.isEdit ? '/api/health/knowledge/update' : '/api/health/knowledge/add'
+      this.$axios.post(url, this.dialog.form).then(res => {
+        if (res.data.code === 200) {
+          this.$message.success('已保存')
+          this.dialog.visible = false
+          this.fetchList()
+        } else {
+          this.$message.error(res.data.message || '保存失败')
+        }
+      }).finally(() => { this.dialog.saving = false })
+    },
+    deleteRow(row) {
+      this.$confirm('确认删除"' + row.itemName + '"?', '提示', { confirmButtonText: '删除', cancelButtonText: '取消', type: 'warning' }).then(function() {
+        this.$axios.post('/api/health/knowledge/delete', { id: row.id }).then(function(res) {
+          if (res.data.code === 200) {
+            this.$message.success('已删除')
+            this.fetchList()
+          }
+        }.bind(this))
+      }.bind(this)).catch(function() {})
+    },
+    importData() {
+      this.$confirm('将从参考JSON导入知识库数据,是否继续?', '提示', { confirmButtonText: '导入', cancelButtonText: '取消' }).then(function() {
+        this.$axios.post('/api/health/knowledge/import', {}).then(function(res) {
+          if (res.data.code === 200) {
+            this.$message.success(res.data.data || '导入完成')
+            this.fetchList()
+          }
+        }.bind(this))
+      }.bind(this)).catch(function() {})
+    }
+  }
+}
+</script>
+
+<style scoped>
+.knowledge-page { padding: 20px; }
+</style>