11 Komitmen 79c6c0e5a1 ... 9e147c672b

Pembuat SHA1 Pesan Tanggal
  wangyang 9e147c672b update 5 tahun lalu
  wangyang 044aea1dde 优化食物营养素单位录入 5 tahun lalu
  wangyang 0ec89dfdd1 食物营养素增加nrv%字段 5 tahun lalu
  wangyang f116c7cd10 修复食物规格营养素含量计算错误的bug 5 tahun lalu
  wangyang c0994f4ee5 营养素增加nrv字段 5 tahun lalu
  wangyang 5357e03bcd 删除食物营养素关联前先判断删除是否影响food normolizedUnit 5 tahun lalu
  wangyang 317232d74c update 5 tahun lalu
  wangyang 9f432e24f2 优化食物营养素计量录入,允许录入范围和误差 5 tahun lalu
  wangyang 2b82f74c5e 修复信息来源相关bug 5 tahun lalu
  wangyang 66beba0bf5 完成食物规格营养素计算 5 tahun lalu
  wangyang 1978af867a update 5 tahun lalu

+ 20 - 2
src/api/food.js

@@ -70,6 +70,14 @@ export function updateFoodNutrientSort(id, nutrientId, data) {
   })
 }
 
+// 删除食物营养素确认
+export function confirmDeleteFoodNutrient(id, nutrientId) {
+  return request({
+    url: `/api/foods/${id}/nutrients/${nutrientId}/confirm-delete`,
+    method: 'post'
+  })
+}
+
 // 删除食物关联的营养素
 export function removeFoodNutrient(foodId, nutrientId) {
   return request({
@@ -147,10 +155,20 @@ export function createOrUpdateFoodUnit(id, data) {
 }
 
 // 获取食物基础单位转换关系
-export function getFoodUnits(id) {
+export function getFoodUnits(id, params) {
   return request({
     url: `/api/foods/${id}/units`,
-    method: 'get'
+    method: 'get',
+    params
+  })
+}
+
+// 食物单位转换
+export function convertFoodUnit(id, data) {
+  return request({
+    url: `/api/foods/${id}/units/convert`,
+    method: 'post',
+    data
   })
 }
 

+ 8 - 0
src/api/nutrient.js

@@ -43,3 +43,11 @@ export function updateSortOrder(id, data) {
     data
   })
 }
+
+// 获取营养素单位列表
+export function getNutrientUnits(id) {
+  return request({
+    url: `/api/nutrients/${id}/units`,
+    method: 'get'
+  })
+}

+ 2 - 1
src/views/food/components/FoodDetail.vue

@@ -90,7 +90,7 @@ export default {
   },
   data() {
     return {
-      postForm: { id2: '', name: '', img: '', description: ''},
+      postForm: { id2: '', name: '', img: '', description: '', ep: 100 },
       loading: false,
       rules: {
         name: [{ required: true, message: '名称不能为空', trigger: 'blur' }]
@@ -113,6 +113,7 @@ export default {
       this.foodId = this.$route.params && this.$route.params.id
       this.fetchData(this.foodId)
     }
+    this.queryTemplates()
   },
   methods: {
     fetchData(id) {

+ 116 - 11
src/views/food/nutrient.vue

@@ -16,8 +16,24 @@
       >
         <el-option v-for="item in nutrients" :key="item.id" :label="item.name" :value="item.id" />
       </el-select>
-      营养素计量:
+      计量:
+      <el-radio v-model="params.radio" :label="0" @change="radioChange(params)">值</el-radio>
+      <el-radio v-model="params.radio" :label="1" @change="radioChange(params)">范围</el-radio>
+      <el-radio v-model="params.radio" :label="2" @change="radioChange(params)">误差</el-radio>
+      <div v-if="params.radio === 1" style="display: inline-block">
+        大于等于:
+        <el-input v-model="params.quantityMin" style="width: 80px;" class="filter-item" />
+        小于等于:
+        <el-input v-model="params.quantityMax" style="width: 80px;" class="filter-item" />
+      </div>
+      <div v-else-if="params.radio === 2" style="display: inline-block">
+        基准:
+        <el-input v-model="params.quantity" style="width: 80px;" class="filter-item" />
+        ±:
+        <el-input v-model="params.stdError" style="width: 80px;" class="filter-item" />
+      </div>
       <el-input
+        v-else
         v-model="params.quantity"
         style="width: 80px;"
         class="filter-item"
@@ -25,9 +41,15 @@
       <el-autocomplete
         class="inline-input"
         v-model="params.unit"
-        :fetch-suggestions="queryUnits"
+        :fetch-suggestions="queryNutrientUnits"
         placeholder="单位关键词"
       />
+      NRV%:
+      <el-input
+        v-model="params.nrvPercent"
+        style="width: 80px;"
+        class="filter-item"
+      />
       Nv_Spec计量:
       <el-input
         v-model="params.nvSpec"
@@ -70,12 +92,32 @@
           <span>{{ row.nutrientName }}</span>
         </template>
       </el-table-column>
-      <el-table-column label="营养素计量" align="center" width="80">
+      <el-table-column label="营养素计量" align="center" :width="150">
         <template slot-scope="{row}">
           <template v-if="row.edit">
-            <el-input v-model="row.quantity" class="filter-item" />
+            <el-radio v-model="row.radio" :label="0" @change="radioChange(row)">值</el-radio>
+            <el-radio v-model="row.radio" :label="1" @change="radioChange(row)">范围</el-radio>
+            <el-radio v-model="row.radio" :label="2" @change="radioChange(row)">误差</el-radio>
+            <div v-if="row.radio === 1" style="display: inline-block">
+              大于等于:
+              <el-input v-model="row.quantityMin" />
+              小于等于:
+              <el-input v-model="row.quantityMax" />
+            </div>
+            <div v-else-if="row.radio === 2" style="display: inline-block">
+              基准:
+              <el-input v-model="row.quantity" />
+              ±:
+              <el-input v-model="row.stdError" />
+            </div>
+            <el-input
+              v-else
+              v-model="row.quantity"
+              style="width: 80px;"
+              class="filter-item"
+            />
           </template>
-          <span v-else>{{ row.quantity }}</span>
+          <span v-else>{{ row | nutrientQuantityFilter }}</span>
         </template>
       </el-table-column>
       <el-table-column label="计量单位" align="center" width="150">
@@ -86,6 +128,14 @@
           <span v-else>{{ row.unit }}</span>
         </template>
       </el-table-column>
+      <el-table-column label="NRV%" align="center" width="80">
+        <template slot-scope="{row}">
+          <template v-if="row.edit">
+            <el-input v-model="row.nrvPercent"/>
+          </template>
+          <span v-else>{{ row.nrvPercent }}</span>
+        </template>
+      </el-table-column>
       <el-table-column label="Nv_Spec" align="center" width="80">
         <template slot-scope="{row}">
           <template v-if="row.edit">
@@ -141,10 +191,10 @@
             </el-button>
           </template>
           <template v-else>
-            <el-button size="mini" type="primary" @click="row.edit=true">编辑</el-button>
+            <el-button size="mini" type="primary" @click="handleEdit(row)">编辑</el-button>
             <el-button size="mini" type="primary" @click="updateSort(row, 0)">上移</el-button>
             <el-button size="mini" type="primary" @click="updateSort(row, 1)">下移</el-button>
-            <el-button size="mini" type="danger" @click="removeNutrient(row)">
+            <el-button size="mini" type="danger" @click="confirmRemoveNutrient(row)">
               删除
             </el-button>
           </template>
@@ -156,8 +206,8 @@
 
 <script>
 import { getNutrientList, addFoodNutrient, updateFoodNutrient, removeFoodNutrient,
-  updateFoodNutrientSort } from '@/api/food'
-import { getList } from '@/api/nutrient'
+  updateFoodNutrientSort, confirmDeleteFoodNutrient } from '@/api/food'
+import { getList, getNutrientUnits } from '@/api/nutrient'
 import { getList as getUnits } from '@/api/unit'
 
 export default {
@@ -167,6 +217,21 @@ export default {
     this.fetchData()
     this.queryNutrients()
   },
+  filters: {
+    nutrientQuantityFilter(row) {
+      if (row.stdError) {
+        return `${row.quantity} ± ${row.stdError}`
+      }else if (row.quantityMin && row.quantityMax) {
+        return `${row.quantityMin} ~ ${row.quantityMax}`
+      } else if (row.quantityMin) {
+        return `≥${row.quantityMin}`
+      } else if (row.quantityMax) {
+        return `≤${row.quantityMax}`
+      } else {
+        return row.quantity
+      }
+    }
+  },
   mounted() {
     this.$nextTick(() => {
       this.$refs.nutrientSelect.focus()
@@ -180,7 +245,7 @@ export default {
       foodId: '',
       list: [],
       nutrients: [],
-      params: {},
+      params: { source: '营养标签', radio: 0 },
       loading: false,
       unitLoading: false,
       sources: [{ value: "营养标签" }, { value: "食品官方资料" }, { value: "计算值" }]
@@ -204,7 +269,7 @@ export default {
     addNutrient() {
       this.params.foodId = this.foodId
       addFoodNutrient(this.foodId, this.params).then(res => {
-        this.params = {}
+        this.params = { source: '营养标签', radio: 0 }
         this.fetchData()
         this.$notify.success('添加营养素成功')
         this.$refs.nutrientSelect.focus()
@@ -220,6 +285,23 @@ export default {
         this.$message.error(res.data.message)
       })
     },
+    confirmRemoveNutrient(row) {
+      confirmDeleteFoodNutrient(row.foodId, row.nutrientId).then(res => {
+        if (!res.data) {
+          this.$confirm('由于单位转换的原因,删除此条营养素关联数据,将会导致该食物被删除,是否继续?', '提示', {
+            confirmButtonText: '确定',
+            cancelButtonText: '取消',
+            type: 'warning'
+          }).then(() => {
+            this.removeNutrient(row)
+          })
+        } else {
+          this.removeNutrient(row)
+        }
+      }).catch(res => {
+        this.$message.error(res.data.message)
+      })
+    },
     removeNutrient(row) {
       removeFoodNutrient(row.foodId, row.nutrientId).then(res => {
         this.fetchData()
@@ -236,6 +318,13 @@ export default {
         this.nutrients = []
       })
     },
+    queryNutrientUnits(query, cb) {
+      let units = []
+      getNutrientUnits(this.params.nutrientId).then(res => {
+        res.data.forEach(item => units.push({ value: item }))
+        cb(units)
+      })
+    },
     queryUnits(query, cb) {
       let units = []
       getUnits({ query }).then(res => {
@@ -260,6 +349,22 @@ export default {
       return (restaurant) => {
         return (restaurant.value.toLowerCase().indexOf(query.toLowerCase()) === 0);
       };
+    },
+    handleEdit(row) {
+      this.$set(row, 'edit', true)
+      if (row.quantityMax || row.quantityMin) {
+        this.$set(row, 'radio', 1)
+      } else if (row.stdError) {
+        this.$set(row, 'radio', 2)
+      } else {
+        this.$set(row, 'radio', 0)
+      }
+    },
+    radioChange(row) {
+      this.$set(row, 'stdError', '')
+      this.$set(row, 'quantityMin', '')
+      this.$set(row, 'quantityMax', '')
+      this.$set(row, 'quantity', '')
     }
   }
 }

+ 42 - 3
src/views/foodModifier/index.vue

@@ -20,6 +20,7 @@
       fit
       highlight-current-row
       style="width: 100%;"
+      @row-click="showDialog"
     >
       <el-table-column type="index" label="序号" align="center" fixed width="60px" />
       <el-table-column label="规格名称" fixed align="center">
@@ -29,7 +30,8 @@
       </el-table-column>
       <el-table-column label="转换关系" align="center">
         <template slot-scope="{row}">
-          <span>{{ row.quantity + row.unit }}</span>
+          <span v-if="row.quantity">{{ row.quantity + row.unit }}</span>
+          <span v-else>无</span>
         </template>
       </el-table-column>
       <el-table-column label="推荐摄入量" align="center">
@@ -79,11 +81,16 @@
         </template>
       </el-table-column>
     </el-table>
+
+    <el-dialog title="营养素计算" :visible.sync="dialogFormVisible">
+      <span v-html="dialogHtml" />
+    </el-dialog>
   </div>
 </template>
 
 <script>
-import { getFoodModifiers, removeFoodModifier, updateFoodModifierSort } from '@/api/food'
+import { getFoodModifiers, removeFoodModifier, updateFoodModifierSort, convertFoodUnit, getNutrientList,
+  getDetail} from '@/api/food'
 
 export default {
   name: 'FoodModifierIndex',
@@ -91,12 +98,16 @@ export default {
     return {
       listLoading: false,
       list: [],
-      foodId: ''
+      food: {},
+      foodId: '',
+      dialogHtml: '',
+      dialogFormVisible: false
     }
   },
   created() {
     this.foodId = this.$route.params && this.$route.params.id
     this.fetchData()
+    this.fetchFood()
   },
   methods: {
     handleCreate() {
@@ -114,6 +125,11 @@ export default {
         this.fetchData()
       })
     },
+    fetchFood() {
+      getDetail(this.foodId).then(res => {
+        this.food = res.data
+      })
+    },
     fetchData() {
       this.listLoading = true
       getFoodModifiers(this.foodId).then(res => {
@@ -131,6 +147,29 @@ export default {
       }).catch(res => {
         this.$message.error(res.data.message)
       })
+    },
+    showDialog(row, column, event) {
+      getNutrientList(row.foodId).then(res => {
+        const nutrients = res.data
+        convertFoodUnit(row.foodId, { fromUnit: row.inInitUnit }).then(res => {
+          if (!res.data.ratio) {
+            return;
+          }
+          this.dialogHtml = `转换关系:1${row.inInitUnit}${this.food.name}=${res.data.ratio}${this.food.normalizedUnit}${this.food.name}<br>`
+          this.dialogHtml += `${row.inInit}${row.inInitUnit}${this.food.name}含有如下营养素:<br>`
+          this.dialogHtml += `******************************<br>`
+          for (let i=0; i < nutrients.length; i++) {
+            const nutrient = nutrients[i]
+            const quantity = nutrient.normalizedQuantity * res.data.ratio * row.inInit
+            this.dialogHtml += `${nutrient.nutrientName}:${quantity}${nutrient.unit}。<br>`
+            this.dialogHtml += `营养素计量为:${nutrient.quantity}${nutrient.unit}。Nv_Spec为:${nutrient.nvSpec}${nutrient.nvSpecUnit}。<br>`
+            this.dialogHtml += `******************************<br>`
+          }
+          this.dialogFormVisible = true
+        })
+      }).catch(res => {
+        this.$message.error('获取数据失败')
+      })
     }
   }
 }

+ 15 - 57
src/views/nutrient/index.vue

@@ -62,24 +62,9 @@
           <span>{{ row.parentName }}</span>
         </template>
       </el-table-column>
-      <el-table-column label="推荐用量类型" align="center" width="70">
+      <el-table-column label="基础单位" align="center" width="150">
         <template slot-scope="{row}">
-          <span>{{ row.recommendType | recommendTypeFilter }}</span>
-        </template>
-      </el-table-column>
-      <el-table-column label="每日推荐量值" align="center" width="70">
-        <template slot-scope="{row}">
-          <span>{{ row.recommend ? `${row.recommend} ${row.recommendUnit}` : '' }}</span>
-        </template>
-      </el-table-column>
-      <el-table-column label="每日推荐量范围起始值" align="center" width="80">
-        <template slot-scope="{row}">
-          <span>{{ row.recommendBegin ? `${row.recommendBegin} ${row.recommendUnit}` : '' }}</span>
-        </template>
-      </el-table-column>
-      <el-table-column label="每日推荐量范围截止值" align="center" width="80">
-        <template slot-scope="{row}">
-          <span>{{ row.recommendEnd ? `${row.recommendEnd} ${row.recommendUnit}` : '' }}</span>
+          <span>{{ row.baseUnit }}</span>
         </template>
       </el-table-column>
       <el-table-column label="创建时间" width="180px" align="center">
@@ -138,32 +123,13 @@
         <el-form-item label="编码" prop="code">
           <el-input v-model="params.code" placeholder="编码" :disabled="dialogStatus === 'edit'" />
         </el-form-item>
-        <el-form-item label="推荐用量类型" prop="recommendType">
-          <el-radio v-model="params.recommendType" :label="0">值</el-radio>
-          <el-radio v-model="params.recommendType" :label="1">范围</el-radio>
-          <el-radio v-model="params.recommendType" :label="2">无</el-radio>
-        </el-form-item>
-        <el-form-item v-if="params.recommendType!==2" label="推荐量单位" prop="recommendUnit">
-          <el-select
-            v-model="params.recommendUnit"
-            filterable
-            remote
-            reserve-keyword
-            placeholder="请选择推荐量单位"
-            :remote-method="queryUnits"
-            :loading="loading"
-          >
-            <el-option v-for="item in units" :key="item.id" :label="item.name" :value="item.name" />
-          </el-select>
-        </el-form-item>
-        <el-form-item v-if="params.recommendType===0" prop="recommend" label="每日推荐摄入量">
-          <el-input v-model="params.recommend" placeholder="每日推荐摄入量" />
-        </el-form-item>
-        <el-form-item v-if="params.recommendType===1" prop="recommendBegin" label="每日推荐摄入量范围-开始值">
-          <el-input v-model="params.recommendBegin" placeholder="每日推荐摄入量范围-开始值" />
-        </el-form-item>
-        <el-form-item v-if="params.recommendType===1" prop="recommendEnd" label="每日推荐摄入量范围-截止值">
-          <el-input v-model="params.recommendEnd" placeholder="每日推荐摄入量范围-截止值" />
+        <el-form-item label="基础单位" prop="baseUnit">
+          <el-autocomplete
+            class="inline-input"
+            v-model="params.baseUnit"
+            :fetch-suggestions="queryUnits"
+            placeholder="单位关键词"
+          />
         </el-form-item>
         <el-form-item label="上级营养素" prop="parentId">
           <el-select
@@ -200,15 +166,10 @@ import SingleImage from '@/components/Upload/SingleImage'
 import { getList, update, remove, create, updateSortOrder } from '@/api/nutrient'
 import { getList as getUnits } from '@/api/unit'
 
-const recommendTypes = { 0: '值', 1: '范围', 2: '无' }
-
 export default {
   name: 'NutrientList',
   components: { Pagination, SingleImage },
   filters: {
-    recommendTypeFilter(value) {
-      return recommendTypes[value]
-    }
   },
   data() {
     return {
@@ -230,7 +191,7 @@ export default {
       rules: {
         name: [{ required: true, message: '名称不允许为空', trigger: 'blur' }]
       },
-      params: { recommendType: 2 },
+      params: { },
       units: [],
       loading: false,
       parentNutrients: []
@@ -254,7 +215,7 @@ export default {
     handleCreateOrUpdate(dialogStatus, row) {
       this.queryUnits()
       this.queryNutrients()
-      this.params = dialogStatus === 'create' ? { recommendType: 2 } : Object.assign({}, row) // copy obj
+      this.params = dialogStatus === 'create' ? {} : Object.assign({}, row)
       this.dialogStatus = dialogStatus
       this.dialogFormVisible = true
       this.$nextTick(() => {
@@ -280,14 +241,11 @@ export default {
         this.fetchData()
       })
     },
-    queryUnits(query) {
-      this.loading = true
+    queryUnits(query, cb) {
+      let units = []
       getUnits({ query }).then(res => {
-        this.units = res.data.list
-        this.loading = false
-      }).catch(res => {
-        this.units = []
-        this.loading = false
+        res.data.list.forEach(item => units.push({ value: item.name }))
+        cb(units)
       })
     },
     queryNutrients(query) {

+ 11 - 4
src/views/nutrientTemplate/detail.vue

@@ -19,7 +19,7 @@
       <el-autocomplete
         class="inline-input"
         v-model="params.unit"
-        :fetch-suggestions="queryUnits"
+        :fetch-suggestions="queryNutrientUnits"
         placeholder="单位关键词"
       />
       Nv_Spec计量:
@@ -155,7 +155,7 @@
 </template>
 
 <script>
-import { getList } from '@/api/nutrient'
+import { getList, getNutrientUnits } from '@/api/nutrient'
 import { getList as getUnits } from '@/api/unit'
 import { getNutrients, addTemplateNutrient, updateTemplateNutrient, removeNutrient,
   updateTemplateNutrientSort, updateBatchDetailSorts } from '@/api/nutrientTemplate'
@@ -172,7 +172,7 @@ export default {
       list: [],
       units: [],
       nutrients: [],
-      params: {},
+      params: { source: '营养标签' },
       loading: false,
       unitLoading: false,
       sources: [{ value: "营养标签" }, { value: "食品官方资料" }, { value: "计算值" }],
@@ -198,7 +198,7 @@ export default {
         addTemplateNutrient(this.templateId, this.params).then(res => {
           this.fetchNutrients()
           this.$notify.success('添加成功')
-          this.params = {}
+          this.params = { source: '营养标签' }
           this.$nextTick(() => {
             this.$refs['nutrientSelect'].focus()
           })
@@ -277,6 +277,13 @@ export default {
         return (restaurant.value.toLowerCase().indexOf(query.toLowerCase()) === 0);
       };
     },
+    queryNutrientUnits(query, cb) {
+      let units = []
+      getNutrientUnits(this.params.nutrientId).then(res => {
+        res.data.forEach(item => units.push({ value: item }))
+        cb(units)
+      })
+    },
     setSort() {
       const el = this.$refs.detailTable.$el.querySelectorAll('.el-table__body-wrapper > table > tbody')[0]
       this.sortable = Sortable.create(el, {