Explorar el Código

fix(web): 指标管理页修复 $http 未定义白屏 + 补五维归属/数据来源字段

- 根因: 页面用 this.$http.post(),但项目实际用 @/utils/request 封装,
  main.js 从未注册 $http(Vue.prototype 仅 $confirm/$alert/$message 等)
  => Cannot read properties of undefined (reading "post") 页面白屏
- api/indicator.js 补 createIndicator/updateIndicator/deleteIndicator 封装
  (listIndicatorDefinitions 已有),页面改用封装 + res.code===200 判定
- indicators/index.vue 表单补字段(对应表 indicator_definitions 已存在列):
  * 五维归属 dimension_code: body(身)/mind(智)/wisdom(富)/action(行)/wealth(心),空=通用
  * 数据来源 source_kind: report_indicator/bacteria/food_index/dimension_score
- 表格新增两列展示(维度/来源中文映射),空串提交前归一化为 null(语义"通用"存 NULL)
- 模板编译 + script/API 语法校验通过
Xiaogang Liao hace 1 semana
padre
commit
85bfc4e697
Se han modificado 2 ficheros con 72 adiciones y 8 borrados
  1. 12 0
      cfc-web/src/api/indicator.js
  2. 60 8
      cfc-web/src/views/admin/indicators/index.vue

+ 12 - 0
cfc-web/src/api/indicator.js

@@ -4,6 +4,18 @@ export function listIndicatorDefinitions(params) {
   return request({ url: '/api/admin/indicator/list', method: 'post', data: params })
 }
 
+export function createIndicator(data) {
+  return request({ url: '/api/admin/indicator/create', method: 'post', data })
+}
+
+export function updateIndicator(data) {
+  return request({ url: '/api/admin/indicator/update', method: 'post', data })
+}
+
+export function deleteIndicator(data) {
+  return request({ url: '/api/admin/indicator/delete', method: 'post', data })
+}
+
 export function saveIndicatorCorrelation(data) {
   return request({ url: '/api/admin/indicator/correlation/save', method: 'post', data })
 }

+ 60 - 8
cfc-web/src/views/admin/indicators/index.vue

@@ -42,6 +42,24 @@
           <span class="sort-header" @click.stop="onSortClick('name', $event)">名称<span v-if="sortIndex('name') !== null" class="sort-index" :class="sortClass('name') === 'sort-asc' ? 'sort-asc-badge' : 'sort-desc-badge'">{{ sortIndex('name') }}</span>{{ sortIcon('name') }}</span>
         </template>
       </el-table-column>
+      <el-table-column prop="dimensionCode" label="五维归属" width="100">
+        <template slot-scope="{ row }">
+          <span v-if="row.dimensionCode">{{ dimensionLabel(row.dimensionCode) }}</span>
+          <span v-else class="text-muted">通用</span>
+        </template>
+        <template slot="header">
+          <span class="sort-header" @click.stop="onSortClick('dimensionCode', $event)">五维归属<span v-if="sortIndex('dimensionCode') !== null" class="sort-index" :class="sortClass('dimensionCode') === 'sort-asc' ? 'sort-asc-badge' : 'sort-desc-badge'">{{ sortIndex('dimensionCode') }}</span>{{ sortIcon('dimensionCode') }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column prop="sourceKind" label="数据来源" width="120">
+        <template slot-scope="{ row }">
+          <span v-if="row.sourceKind">{{ sourceKindLabel(row.sourceKind) }}</span>
+          <span v-else class="text-muted">—</span>
+        </template>
+        <template slot="header">
+          <span class="sort-header" @click.stop="onSortClick('sourceKind', $event)">数据来源<span v-if="sortIndex('sourceKind') !== null" class="sort-index" :class="sortClass('sourceKind') === 'sort-asc' ? 'sort-asc-badge' : 'sort-desc-badge'">{{ sortIndex('sourceKind') }}</span>{{ sortIcon('sourceKind') }}</span>
+        </template>
+      </el-table-column>
       <el-table-column prop="dataType" label="数据类型" width="100">
         <template slot="header">
           <span class="sort-header" @click.stop="onSortClick('dataType', $event)">数据类型<span v-if="sortIndex('dataType') !== null" class="sort-index" :class="sortClass('dataType') === 'sort-asc' ? 'sort-asc-badge' : 'sort-desc-badge'">{{ sortIndex('dataType') }}</span>{{ sortIcon('dataType') }}</span>
@@ -96,6 +114,23 @@
         <el-form-item label="名称" required>
           <el-input v-model="form.name" />
         </el-form-item>
+        <el-form-item label="五维归属">
+          <el-select v-model="form.dimensionCode" class="form-full" clearable placeholder="不选=通用(不关联具体五维)">
+            <el-option label="身" value="body" />
+            <el-option label="智" value="mind" />
+            <el-option label="富" value="wisdom" />
+            <el-option label="行" value="action" />
+            <el-option label="心" value="wealth" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="数据来源">
+          <el-select v-model="form.sourceKind" class="form-full" clearable placeholder="不选=通用">
+            <el-option label="报告指标" value="report_indicator" />
+            <el-option label="细菌/菌群" value="bacteria" />
+            <el-option label="食物指数" value="food_index" />
+            <el-option label="五维得分" value="dimension_score" />
+          </el-select>
+        </el-form-item>
         <el-form-item label="数据类型" required>
           <el-select v-model="form.dataType" class="form-full">
             <el-option label="枚举" value="enum" />
@@ -126,6 +161,7 @@
 
 <script>
 import SortMixin from '@/mixins/SortMixin'
+import { listIndicatorDefinitions, createIndicator, updateIndicator, deleteIndicator } from '@/api/indicator'
 
 export default {
   name: 'IndicatorsIndex',
@@ -153,10 +189,12 @@ export default {
         dataType: 'enum',
         options: '',
         unit: '',
+        dimensionCode: '',
+        sourceKind: '',
         sortOrder: 0,
         status: 1
       },
-      sortableColumns: { id: 'ID', domain: '领域', category: '分类', code: '编码', name: '名称', dataType: '数据类型', options: '可选值', sortOrder: '排序', status: '状态' }
+      sortableColumns: { id: 'ID', domain: '领域', category: '分类', code: '编码', name: '名称', dataType: '数据类型', options: '可选值', dimensionCode: '五维归属', sourceKind: '数据来源', sortOrder: '排序', status: '状态' }
     }
   },
   mounted() {
@@ -166,6 +204,14 @@ export default {
     loadList() {
       this.loadData()
     },
+    dimensionLabel(code) {
+      const map = { body: '身', mind: '智', wisdom: '富', action: '行', wealth: '心' }
+      return map[code] || code
+    },
+    sourceKindLabel(kind) {
+      const map = { report_indicator: '报告指标', bacteria: '细菌/菌群', food_index: '食物指数', dimension_score: '五维得分' }
+      return map[kind] || kind
+    },
     loadData() {
       this.loading = true
       var params = {
@@ -173,8 +219,8 @@ export default {
         status: 1
       }
       if (this.sortState.length > 0) params.sort = this.sortState
-      this.$http.post('/api/admin/indicator/list', params).then(res => {
-        if (res.code === 0) {
+      listIndicatorDefinitions(params).then(res => {
+        if (res.code === 200) {
           this.list = res.data || []
           if (this.filterKeyword) {
             var kw = this.filterKeyword.toLowerCase()
@@ -201,6 +247,8 @@ export default {
           dataType: 'enum',
           options: '',
           unit: '',
+          dimensionCode: '',
+          sourceKind: '',
           sortOrder: 0,
           status: 1
         }
@@ -209,9 +257,13 @@ export default {
     },
     handleSave() {
       this.saving = true
-      var url = this.form.id ? '/api/admin/indicator/update' : '/api/admin/indicator/create'
-      this.$http.post(url, this.form).then(res => {
-        if (res.code === 0) {
+      var payload = Object.assign({}, this.form)
+      // 空串归一化为 null,避免数据库存 ''(语义"通用"应存 NULL)
+      if (payload.dimensionCode === '') payload.dimensionCode = null
+      if (payload.sourceKind === '') payload.sourceKind = null
+      var req = this.form.id ? updateIndicator(payload) : createIndicator(payload)
+      req.then(res => {
+        if (res.code === 200) {
           this.$message.success('保存成功')
           this.dialogVisible = false
           this.loadData()
@@ -222,8 +274,8 @@ export default {
     },
     handleDelete(row) {
       this.$confirm('确定删除该指标?', '提示', { type: 'warning' }).then(() => {
-        this.$http.post('/api/admin/indicator/delete', { id: row.id }).then(res => {
-          if (res.code === 0) {
+        deleteIndicator({ id: row.id }).then(res => {
+          if (res.code === 200) {
             this.$message.success('删除成功')
             this.loadData()
           } else {