5 Commits c40a32f50d ... 1355a89a51

Tác giả SHA1 Thông báo Ngày
  wangyang 1355a89a51 配置测试环境 5 năm trước cách đây
  wangyang 7deb1791c3 完成食物复制 5 năm trước cách đây
  wangyang a2dda00dca 完成食物营养素从近似食物导入 5 năm trước cách đây
  wangyang d9d0fd44a6 完成食物规格从近似食物导入 5 năm trước cách đây
  wangyang f2d3425723 食物增加状态:上架下架 5 năm trước cách đây

+ 6 - 0
.env.test

@@ -0,0 +1,6 @@
+# just a flag
+ENV = 'production'
+PORT = '9532'
+
+# base api
+VUE_APP_BASE_API = '//feuc-test.liveplus.online'

+ 2 - 1
package.json

@@ -5,7 +5,8 @@
   "author": "Pan <panfree23@gmail.com>",
   "scripts": {
     "dev": "vue-cli-service serve",
-    "build:prod": "vue-cli-service build",
+    "build:prod": "vue-cli-service build --mode production",
+    "build:test": "vue-cli-service build --mode test",
     "build:stage": "vue-cli-service build --mode staging",
     "preview": "node build/index.js --preview",
     "svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml",

+ 27 - 0
src/api/food.js

@@ -206,3 +206,30 @@ export function importNutrientsFromTemplate(id, data) {
     data
   })
 }
+
+// 食物从近似食物添加营养素
+export function importNutrientFromSimilarFood(id, data) {
+  return request({
+    url: `/api/foods/${id}/nutrients/import-from-food`,
+    method: 'post',
+    data
+  })
+}
+
+// 食物从近似食物导入规格
+export function importModifiersFromSimilarFood(id, data) {
+  return request({
+    url: `/api/foods/${id}/modifiers/import-from-food`,
+    method: 'post',
+    data
+  })
+}
+
+// 复制相似食物
+export function copyFromSimilarFood(data) {
+  return request({
+    url: `/api/foods/copy-food`,
+    method: 'post',
+    data
+  })
+}

+ 0 - 7
src/components/FloatingWindow/index.vue

@@ -45,7 +45,6 @@ export default {
   },
   methods: {
     start(e) {
-      console.log("start");
       e.preventDefault();   //阻止默认的拖拽
       if (e.button == 0) {
         this.canDrag = this.ableMove;
@@ -55,7 +54,6 @@ export default {
       }
     },
     move(e) {
-      console.log("move");
       if (this.canDrag == true) {
         this.isClick = false;
         let x = e.clientX - this.x0;
@@ -78,17 +76,14 @@ export default {
     },
     stop() {
       this.canDrag = false;
-      console.log("stop");
       this.show();
     },
     enter() {
-      console.log("enter");
       if (this.hoverShow) {
         this.show();
       }
     },
     leave() {
-      console.log("leave");
       this.canDrag = false;
       this.hide();
     },
@@ -105,7 +100,6 @@ export default {
     },
     hide() {
       if (this.ableHide) {
-        console.log("hide");
         let img = document.querySelector(".suspendBox");
         let box = document.querySelector(".outBox");
         if (!this.canDrag && img.offsetLeft < this.distance) {
@@ -116,7 +110,6 @@ export default {
       }
     },
     clickEvent() {      //悬浮窗的点击事件
-      console.log("悬浮窗的点击事件");
       this.$emit("clickHandle")
     },
   },

+ 58 - 24
src/views/food/components/FoodDetail.vue

@@ -36,6 +36,10 @@
         <el-form-item label="GL:" prop="gl" style="margin-bottom: 40px;width: 60%">
           <el-input v-model="postForm.gl" placeholder="请输入gl" style="width: 60%" />
         </el-form-item>
+        <el-form-item label="状态:" prop="status" style="margin-bottom: 40px;width: 60%">
+          <el-radio :label="1" v-model="postForm.status">上架</el-radio>
+          <el-radio :label="0" v-model="postForm.status">下架</el-radio>
+        </el-form-item>
         <el-form-item label="食物种类:" prop="categoryId" style="margin-bottom: 40px;width: 60%">
           <el-cascader
             v-model="postForm.categoryId"
@@ -75,26 +79,28 @@
           保存
         </el-button>
         <el-button v-loading="loading" style="margin-left: 10px;" type="success" v-if="!isEdit" @click="handleDialog">
-          保存并从模板导入营养素
+          保存并导入营养素
         </el-button>
       </div>
     </el-form>
 
-    <el-dialog title="从模板导入营养素" :visible.sync="dialogFormVisible">
-      <el-form ref="dialogForm" label-position="left" label-width="60px" style="width: 80%; margin-left:50px;">
-        <el-form-item label="模板:" prop="templateId" >
+    <el-dialog title="导入营养素" :visible.sync="dialogFormVisible" v-loading="importLoading">
+      <el-form ref="dialogForm" label-position="left" style="width: 80%; margin-left:50px;">
+        <el-radio :label="0" v-model="importType" @change="handleImportTypeChange" >从模板导入</el-radio>
+        <el-radio :label="1" v-model="importType" @change="handleImportTypeChange" style="margin-bottom: 10px">从食物导入</el-radio>
+        <el-form-item prop="importId">
           <el-select
-            v-model="templateId"
+            v-model="importId"
             class="filter-item"
             filterable
             remote
             reserve-keyword
-            placeholder="请输入模板关键词"
-            :loading="templateLoading"
-            :remote-method="queryTemplates"
+            placeholder="请输入关键词"
+            :loading="importLoading"
+            :remote-method="queryImportItems"
             style="width: 100%"
           >
-            <el-option v-for="item in templates" :key="`template${item.id}`" :label="item.name" :value="item.id" />
+            <el-option v-for="item in importItems" :key="`import${item.id}`" :label="item.name" :value="item.id" />
           </el-select>
         </el-form-item>
       </el-form>
@@ -102,7 +108,7 @@
         <el-button @click="dialogFormVisible = false">
           取消
         </el-button>
-        <el-button type="primary" @click="submitFromTemplate">
+        <el-button type="primary" @click="submitImport">
           提交
         </el-button>
       </div>
@@ -111,7 +117,7 @@
 </template>
 
 <script>
-import { create, getDetail, update } from '@/api/food'
+import { create, getDetail, getList as getFoodList, update } from '@/api/food'
 import Dropzone from '@/components/Dropzone'
 import SingleImage from '@/components/Upload/SingleImage'
 import FloatingWindow from '@/components/FloatingWindow'
@@ -129,23 +135,24 @@ export default {
   },
   data() {
     return {
-      postForm: { id2: '', name: '', img: '', description: '', ep: 100 },
+      postForm: { id2: '', name: '', img: '', description: '', ep: 100, status: 0 },
       loading: false,
       rules: {
         name: [{ required: true, message: '名称不能为空', trigger: 'blur' }]
       },
       foodId: '',
-      dialogFormVisible: false,
-      templateLoading:false,
-      templates: [],
-      templateId: '',
       foodCategories: [],
       categoriesProp: {
         value: 'id',
         expandTrigger: 'hover'
       },
       isFixed: false,
-      offsetTop: 0
+      offsetTop: 0,
+      dialogFormVisible: false,
+      importLoading: false,
+      importType: 0,
+      importId: '',
+      importItems: []
     }
   },
   mounted() {
@@ -212,23 +219,50 @@ export default {
         this.postForm.otherImages = [file.fileUrl]
       }
     },
+    handleImportTypeChange() {
+      this.importId = ''
+      this.importItems = []
+      this.queryImportItems()
+    },
+    queryImportItems(query=''){
+      this.importType === 0 ? this.queryTemplates(query) : this.queryFoods(query)
+    },
     queryTemplates(query='') {
+      this.importLoading = true
       getNutrientTemplates({ query }).then(res => {
-        this.templates = res.data.list
-        if (this.templates.length > 0) {
-          this.templateId = this.templates[0].id
+        this.importItems = res.data.list
+        if (this.importItems.length > 0) {
+          this.importId = this.importItems[0].id
         }
+        this.importLoading = false
       }).catch(() => {
-        this.templates = []
+        this.importItems = []
+        this.importLoading = false
+      })
+    },
+    queryFoods(query='') {
+      this.importLoading = true
+      getFoodList({ query: query, orderBy: 0, pageNum: 1, pageSize: 20 }).then(res => {
+        this.importItems = res.data.list
+        this.importLoading = false
+      }).catch(() => {
+        this.importItems = []
+        this.importLoading = false
       })
     },
     handleDialog() {
-      this.templateId = ''
+      this.importId = ''
+      this.importType = 0
+      this.importItems = []
       this.queryTemplates()
       this.dialogFormVisible = true
     },
-    submitFromTemplate() {
-      this.postForm.templateId = this.templateId
+    submitImport() {
+      if (this.importType === 0 && this.importId) {
+        this.postForm.templateId = this.importId
+      } else if (this.importType === 1 && this.importId) {
+        this.postForm.importFoodId = this.importId
+      }
       this.submitForm()
     },
     handleChange(data) {

+ 48 - 4
src/views/food/index.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="app-container" v-loading="uploadLoading">
     <div class="filter-container">
-      <el-row>
+      <el-row style="margin-bottom: 5px">
         <el-input
           v-model="listQuery.query"
           placeholder="请输入检索词"
@@ -97,6 +97,11 @@
           <span>{{ row.dataSource }}</span>
         </template>
       </el-table-column>
+      <el-table-column label="状态" align="center" width="100px">
+        <template slot-scope="{row}">
+          <span>{{ row.status | statusFilter }}</span>
+        </template>
+      </el-table-column>
       <el-table-column label="创建时间" width="180px" align="center">
         <template slot-scope="{row}">
           <span>{{ row.createTime }}</span>
@@ -110,11 +115,12 @@
       <el-table-column label="操作" align="center" class-name="small-padding" fixed="right" width="90">
         <template slot-scope="{row,$index}">
           <el-dropdown @command="handleCommand">
-            <el-button size="small" @click.stop="{}" type="primary">
+            <el-button :disabled="row.status === -1" size="small" @click.stop="{}" type="primary">
               操作<i class="el-icon-arrow-down el-icon--right"></i>
             </el-button>
             <el-dropdown-menu slot="dropdown">
               <el-dropdown-item :command="{row: row, command: 'update'}">更新</el-dropdown-item>
+              <el-dropdown-item :command="{row: row, command: 'copy'}">复制</el-dropdown-item>
               <el-dropdown-item :command="{row: row, command: 'manageNutrients'}">营养素关联</el-dropdown-item>
               <el-dropdown-item :command="{row: row, command: 'manageUnits'}">单位管理</el-dropdown-item>
               <el-dropdown-item :command="{row: row, command: 'manageModifiers'}">规格管理</el-dropdown-item>
@@ -156,16 +162,33 @@
         </el-button>
       </div>
     </el-dialog>
+
+    <el-dialog title="复制食物" width="40%" :visible.sync="copyDialogVisible">
+      <el-form ref="dialogForm" label-position="left" label-width="90px" style="width: 80%; margin-left:50px;">
+        <el-form-item label="新食物名称: " prop="foodName">
+          <el-input v-model="params.foodName" placeholder="请输入新食物名称" />
+        </el-form-item>
+      </el-form>
+      <div slot="footer" >
+        <el-button @click="copyDialogVisible = false">
+          取消
+        </el-button>
+        <el-button type="primary" @click="copyFood">
+          确认
+        </el-button>
+      </div>
+    </el-dialog>
   </div>
 </template>
 
 <script>
 import Pagination from '@/components/Pagination'
-import { getList, remove } from '@/api/food'
+import { getList, remove, copyFromSimilarFood } from '@/api/food'
 import axios from 'axios'
 import { getToken } from '@/utils/auth'
 
 const foodTypes = { 0: '主材', 1: '辅材' }
+const foodStatus = { '-1': '删除', '0': '下架', '1': '上架' }
 
 export default {
   name: 'FoodList',
@@ -173,6 +196,9 @@ export default {
   filters: {
     foodTypeFilter(value) {
       return foodTypes[value]
+    },
+    statusFilter(value) {
+      return foodStatus[value.toString()]
     }
   },
   data() {
@@ -191,7 +217,9 @@ export default {
         pageSize: 20
       },
       uploadFile: null,
-      uploadLoading: false
+      uploadLoading: false,
+      copyDialogVisible: false,
+      params: {}
     }
   },
   created() {
@@ -225,6 +253,7 @@ export default {
       remove(row.id).then(res => {
         this.$notify({ title: '成功', message: '删除成功', type: 'success', duration: 2000 })
         this.list.splice(index, 1)
+        this.fetchData()
       }).catch(res => {
         this.$message.error(res.data.message)
       })
@@ -255,11 +284,26 @@ export default {
       fromData.append('file', file.raw)
       this.uploadFile = fromData
     },
+    handleCopy(data) {
+      this.params = { foodId: data.id }
+      this.copyDialogVisible = true
+    },
+    copyFood() {
+      copyFromSimilarFood(this.params).then(res => {
+        this.$notify.success('提交成功')
+        this.$router.push({ path: `/food/edit/${res.data}`})
+      }).catch(res => {
+        this.$message.error(res.data.message)
+      })
+    },
     handleCommand(data) {
       switch (data.command) {
         case 'update':
           this.handleUpdate(data.row)
           break
+        case 'copy':
+          this.handleCopy(data.row)
+          break
         case 'manageNutrients':
           this.manageNutrients(data.row)
           break

+ 56 - 25
src/views/food/nutrient.vue

@@ -99,7 +99,7 @@
           style="margin-left: 10px;"
           type="success"
           @click="handleImport">
-          从模板导入营养素
+          导入营养素
         </el-button>
       </el-row>
     </div>
@@ -231,20 +231,23 @@
       </el-table-column>
     </el-table>
 
-    <el-dialog title="从模板导入营养素" :visible.sync="dialogFormVisible" v-loading="templateLoading">
-      <el-form ref="dialogForm" label-position="left" label-width="60px" style="width: 80%; margin-left:50px;">
-        <el-form-item label="模板:" prop="templateId">
+    <el-dialog title="导入营养素" :visible.sync="dialogFormVisible" v-loading="importLoading">
+      <el-form ref="dialogForm" label-position="left" style="width: 80%; margin-left:50px;">
+        <el-radio :label="0" v-model="importType" @change="handleImportTypeChange" >从模板导入</el-radio>
+        <el-radio :label="1" v-model="importType" @change="handleImportTypeChange" style="margin-bottom: 10px">从食物导入</el-radio>
+        <el-form-item prop="importId">
           <el-select
-            v-model="templateId"
+            v-model="importId"
             class="filter-item"
             filterable
             remote
             reserve-keyword
-            placeholder="请输入模板关键词"
-            :remote-method="queryTemplates"
+            placeholder="请输入关键词"
+            :loading="importLoading"
+            :remote-method="queryImportItems"
             style="width: 100%"
           >
-            <el-option v-for="item in templates" :key="`template${item.id}`" :label="item.name" :value="item.id" />
+            <el-option v-for="item in importItems" :key="`import${item.id}`" :label="item.name" :value="item.id" />
           </el-select>
         </el-form-item>
       </el-form>
@@ -252,7 +255,7 @@
         <el-button @click="dialogFormVisible = false">
           取消
         </el-button>
-        <el-button type="primary" @click="submitFromTemplate">
+        <el-button type="primary" @click="submitImport">
           提交
         </el-button>
       </div>
@@ -262,7 +265,8 @@
 
 <script>
 import { getNutrientList, addFoodNutrient, updateFoodNutrient, removeFoodNutrient,
-  updateFoodNutrientSort, confirmDeleteFoodNutrient, importNutrientsFromTemplate } from '@/api/food'
+  updateFoodNutrientSort, confirmDeleteFoodNutrient, importNutrientsFromTemplate,
+  getList as getFoodList, importNutrientFromSimilarFood } from '@/api/food'
 import FloatingWindow from '@/components/FloatingWindow'
 import { getList, getNutrientUnits } from '@/api/nutrient'
 import { getList as getUnits } from '@/api/unit'
@@ -311,9 +315,10 @@ export default {
       unitLoading: false,
       sources: [{ value: "营养标签" }, { value: "食品官方资料" }, { value: "计算值" }],
       dialogFormVisible: false,
-      templateLoading: false,
-      templateId: '',
-      templates: [],
+      importType: 0,
+      importId: '',
+      importLoading: false,
+      importItems: [],
       isFixed: false,
       offsetTop: 0
     }
@@ -445,31 +450,57 @@ export default {
       event.currentTarget.select();
     },
     handleImport() {
-      this.queryTemplates()
-      this.dialogFormVisible = true
       this.query = ''
-      this.templateId = ''
+      this.importId = ''
+      this.importType = 0
+      this.importItems = []
+      this.queryImportItems()
+      this.dialogFormVisible = true
+    },
+    handleImportTypeChange() {
+      this.importId = ''
+      this.importItems = []
+      this.queryImportItems()
+    },
+    queryImportItems(query=''){
+      this.importType === 0 ? this.queryTemplates(query) : this.queryFoods(query)
     },
     queryTemplates(query='') {
+      this.importLoading = true
       getNutrientTemplates({ query }).then(res => {
-        this.templates = res.data.list
-        if (this.templates.length > 0) {
-          this.templateId = this.templates[0].id
+        this.importItems = res.data.list
+        if (this.importItems.length > 0) {
+          this.importId = this.importItems[0].id
         }
+        this.importLoading = false
+      }).catch(() => {
+        this.importItems = []
+        this.importLoading = false
+      })
+    },
+    queryFoods(query='') {
+      this.importLoading = true
+      getFoodList({ query: query, orderBy: 0, pageNum: 1, pageSize: 20 }).then(res => {
+        this.importItems = res.data.list
+        this.importLoading = false
       }).catch(() => {
-        this.templates = []
+        this.importItems = []
+        this.importLoading = false
       })
     },
-    submitFromTemplate() {
-      this.templateLoading = true
-      importNutrientsFromTemplate(this.foodId, { templateId: this.templateId }).then(res => {
+    submitImport() {
+      this.importLoading = true
+      const resPromise = this.importType === 0 ?
+        importNutrientsFromTemplate(this.foodId, { templateId: this.importId }) :
+        importNutrientFromSimilarFood(this.foodId, { foodId: this.importId })
+      resPromise.then(res => {
         this.fetchData()
         this.$notify.success("提交成功")
         this.dialogFormVisible = false
-        this.templateLoading = false
+        this.importLoading = false
       }).catch(res => {
         this.$message.error(res.data.message)
-        this.templateLoading = false
+        this.importLoading = false
         this.dialogFormVisible = false
       })
     },

+ 76 - 0
src/views/foodModifier/components/ModifierImport.vue

@@ -0,0 +1,76 @@
+<template>
+  <div style="overflow: auto">
+    <el-form ref="dialogForm" label-position="left" label-width="60px" style="width: 80%; margin-left:50px;">
+      <el-form-item label="食物:" prop="importFoodId" >
+        <el-select
+          v-model="importFoodId"
+          class="filter-item"
+          filterable
+          remote
+          reserve-keyword
+          placeholder="请输入食物关键词"
+          :loading="foodLoading"
+          :remote-method="queryFoods"
+          style="width: 100%"
+        >
+          <el-option v-for="item in foods" :key="`food${item.id}`" :label="item.name" :value="item.id" />
+        </el-select>
+      </el-form-item>
+    </el-form>
+    <div slot="footer" class="dialog-footer" style="float: right">
+      <el-button @click="closeDialog">
+        取消
+      </el-button>
+      <el-button type="primary" @click="submit">
+        提交
+      </el-button>
+    </div>
+  </div>
+</template>
+
+<script>
+import { getList, importModifiersFromSimilarFood } from '@/api/food'
+
+export default {
+  name: 'ModifierImport',
+  props: {
+    foodId: { type: String }
+  },
+  data() {
+    return {
+      importFoodId: '',
+      foodLoading: false,
+      foods: []
+    }
+  },
+  methods: {
+    queryFoods(query='') {
+      this.foodLoading = true
+      getList({ query: query, orderBy: 0, pageNum: 1, pageSize: 20 }).then(res => {
+        this.foods = res.data.list
+        this.foodLoading = false
+      }).catch(() => {
+        this.foods = []
+        this.foodLoading = false
+      })
+    },
+    closeDialog() {
+      this.foods = []
+      this.importFoodId = ''
+      this.$emit("closeDialog")
+    },
+    submit() {
+      importModifiersFromSimilarFood(this.foodId, { foodId: this.importFoodId }).then(res => {
+        this.$notify.success('提交成功')
+        this.closeDialog()
+      }).catch(res => {
+        this.$message.error(res.data.message)
+      })
+    }
+  }
+}
+</script>
+
+<style scoped>
+
+</style>

+ 30 - 6
src/views/foodModifier/index.vue

@@ -11,6 +11,15 @@
       >
         新建
       </el-button>
+      <el-button
+        class="filter-item"
+        style="margin: 0 10px 20px 0; float: left;"
+        type="success"
+        icon="el-icon-circle-plus-outline"
+        @click="importDialogVisible = true"
+      >
+        从近似食物导入
+      </el-button>
     </div>
 
     <floating-window :able-hide="false" :class="{'is_fixed': isFixed}" >
@@ -62,6 +71,11 @@
           <span>{{ row.inInit + row.inInitUnit }}</span>
         </template>
       </el-table-column>
+      <el-table-column label="CNVR" align="center">
+        <template slot-scope="{row}">
+          <span>{{ row.unitTip }}</span>
+        </template>
+      </el-table-column>
       <el-table-column label="最小刻度" align="center" width="80px">
         <template slot-scope="{row}">
           <span>{{ row.minScale }}</span>
@@ -89,16 +103,16 @@
       </el-table-column>
       <el-table-column label="操作" align="center" class-name="small-padding" fixed="right" width="320">
         <template slot-scope="{row}">
-          <el-button type="primary" size="mini" @click="handleUpdate(row)">
+          <el-button type="primary" size="mini" @click.stop="handleUpdate(row)">
             更新
           </el-button>
-          <el-button type="primary" size="mini" @click="updateSort(row, 0)">
+          <el-button type="primary" size="mini" @click.stop="updateSort(row, 0)">
             上移
           </el-button>
-          <el-button type="primary" size="mini" @click="updateSort(row, 1)">
+          <el-button type="primary" size="mini" @click.stop="updateSort(row, 1)">
             下移
           </el-button>
-          <el-button size="mini" type="danger" @click="handleDelete(row)">
+          <el-button size="mini" type="danger" @click.stop="handleDelete(row)">
             删除
           </el-button>
         </template>
@@ -108,6 +122,10 @@
     <el-dialog title="营养素计算" :visible.sync="dialogFormVisible">
       <span v-html="dialogHtml" />
     </el-dialog>
+
+    <el-dialog title="从近似食物导入" :visible.sync="importDialogVisible">
+      <modifier-import :food-id="foodId" @closeDialog="handleImportDialogClose" />
+    </el-dialog>
   </div>
 </template>
 
@@ -115,10 +133,11 @@
 import { getFoodModifiers, removeFoodModifier, updateFoodModifierSort, convertFoodUnit, getNutrientList,
   getDetail, calculateNutrientsContent } from '@/api/food'
 import FloatingWindow from '@/components/FloatingWindow'
+import ModifierImport from './components/ModifierImport'
 
 export default {
   name: 'FoodModifierIndex',
-  components: { FloatingWindow },
+  components: { FloatingWindow, ModifierImport },
   data() {
     return {
       listLoading: false,
@@ -128,7 +147,8 @@ export default {
       dialogHtml: '',
       dialogFormVisible: false,
       isFixed: false,
-      offsetTop: 0
+      offsetTop: 0,
+      importDialogVisible: false
     }
   },
   mounted() {
@@ -212,6 +232,10 @@ export default {
       if (path) {
         this.$router.push({ path: path })
       }
+    },
+    handleImportDialogClose() {
+      this.fetchData()
+      this.importDialogVisible = false
     }
   }
 }