Foods.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. <template>
  2. <div class="foods admin-page">
  3. <el-card>
  4. <div slot="header">
  5. <span>食材管理</span>
  6. <el-button type="primary" size="mini" style="float:right" @click="openCreate">新建食材</el-button>
  7. <el-input v-model="keyword" placeholder="搜索..." prefix-icon="el-icon-search" style="width:200px" clearable @keyup.enter.native="handleSearch" @clear="handleSearch" />
  8. <el-select
  9. v-model="categoryFilter"
  10. placeholder="分类筛选"
  11. size="mini"
  12. style="float:right;margin-right:12px"
  13. clearable
  14. @change="loadList"
  15. >
  16. <el-option label="全部分类" value="" />
  17. <el-option label="蔬菜" value="蔬菜" />
  18. <el-option label="水果" value="水果" />
  19. <el-option label="肉类" value="肉类" />
  20. <el-option label="水产" value="水产" />
  21. <el-option label="蛋奶" value="蛋奶" />
  22. <el-option label="豆类" value="豆类" />
  23. <el-option label="谷物" value="谷物" />
  24. <el-option label="坚果" value="坚果" />
  25. <el-option label="调味品" value="调味品" />
  26. <el-option label="其他" value="其他" />
  27. </el-select>
  28. </div>
  29. <div style="overflow:auto;max-height:calc(100vh - 300px);">
  30. <el-table :data="list" v-loading="loading" border stripe style="max-height:calc(100vh - 300px);">
  31. <el-table-column prop="id" label="ID" width="80" />
  32. <el-table-column prop="name" label="名称" />
  33. <el-table-column prop="category" label="分类" width="100" />
  34. <el-table-column prop="calories" label="热量(kcal)" width="100" />
  35. <el-table-column prop="protein" label="蛋白质(g)" width="100" />
  36. <el-table-column prop="carbs" label="碳水(g)" width="100" />
  37. <el-table-column prop="fat" label="脂肪(g)" width="100" />
  38. <el-table-column prop="fiber" label="膳食纤维(g)" width="100" />
  39. <el-table-column prop="energyKj" label="能量(kJ)" width="100" />
  40. <el-table-column prop="starch" label="淀粉(g)" width="100" />
  41. <el-table-column prop="cholesterol" label="胆固醇(mg)" width="100" />
  42. <el-table-column label="应季月份" width="180">
  43. <template slot-scope="{ row }">
  44. <span v-if="row.months && row.months.length">{{ row.months.map(function(m) { return m + '月' }).join('、') }}</span>
  45. <span v-else style="color:#999">未设置</span>
  46. </template>
  47. </el-table-column>
  48. <el-table-column prop="status" label="状态" width="80" />
  49. <el-table-column label="操作" width="200" fixed="right">
  50. <template slot-scope="{ row }">
  51. <el-button size="mini" type="primary" @click="editItem(row)">编辑</el-button>
  52. <el-dropdown trigger="hover" @command="(cmd) => handleActionCmd(row, cmd)">
  53. <el-button size="mini">
  54. 更多<i class="el-icon-arrow-down el-icon--right"></i>
  55. </el-button>
  56. <el-dropdown-menu slot="dropdown">
  57. <el-dropdown-item command="delete" icon="el-icon-delete">删除</el-dropdown-item>
  58. </el-dropdown-menu>
  59. </el-dropdown>
  60. </template>
  61. </el-table-column>
  62. </el-table>
  63. </div>
  64. </el-card>
  65. <el-dialog :title="editId ? '编辑食材' : '新建食材'" :visible.sync="dialogVisible" width="600px">
  66. <el-form label-width="120px">
  67. <el-form-item label="名称">
  68. <el-input v-model="form.name" />
  69. </el-form-item>
  70. <el-form-item label="分类">
  71. <el-select v-model="form.category" style="width:100%">
  72. <el-option label="蔬菜" value="蔬菜" />
  73. <el-option label="水果" value="水果" />
  74. <el-option label="肉类" value="肉类" />
  75. <el-option label="水产" value="水产" />
  76. <el-option label="蛋奶" value="蛋奶" />
  77. <el-option label="豆类" value="豆类" />
  78. <el-option label="谷物" value="谷物" />
  79. <el-option label="坚果" value="坚果" />
  80. <el-option label="调味品" value="调味品" />
  81. <el-option label="其他" value="其他" />
  82. </el-select>
  83. </el-form-item>
  84. <el-form-item label="热量(kcal/100g)">
  85. <el-input-number v-model="form.calories" :min="0" style="width:100%" />
  86. </el-form-item>
  87. <el-form-item label="蛋白质(g)">
  88. <el-input-number v-model="form.protein" :min="0" :precision="1" style="width:100%" />
  89. </el-form-item>
  90. <el-form-item label="碳水(g)">
  91. <el-input-number v-model="form.carbs" :min="0" :precision="1" style="width:100%" />
  92. </el-form-item>
  93. <el-form-item label="脂肪(g)">
  94. <el-input-number v-model="form.fat" :min="0" :precision="1" style="width:100%" />
  95. </el-form-item>
  96. <el-form-item label="膳食纤维(g)">
  97. <el-input-number v-model="form.fiber" :min="0" :precision="1" style="width:100%" />
  98. </el-form-item>
  99. <el-form-item label="能量(kJ/100g)">
  100. <el-input-number v-model="form.energyKj" :min="0" style="width:100%" />
  101. </el-form-item>
  102. <el-form-item label="淀粉(g/100g)">
  103. <el-input-number v-model="form.starch" :min="0" :precision="1" style="width:100%" />
  104. </el-form-item>
  105. <el-form-item label="胆固醇(mg/100g)">
  106. <el-input-number v-model="form.cholesterol" :min="0" :precision="1" style="width:100%" />
  107. </el-form-item>
  108. <el-form-item label="单位">
  109. <el-input v-model="form.unit" placeholder="如: 克、个、份" />
  110. </el-form-item>
  111. <el-form-item label="应季月份">
  112. <el-checkbox-group v-model="form.months">
  113. <el-checkbox v-for="m in 12" :key="m" :label="m">{{ m }}月</el-checkbox>
  114. </el-checkbox-group>
  115. </el-form-item>
  116. <el-form-item label="状态">
  117. <el-select v-model="form.status" style="width:100%">
  118. <el-option label="启用" value="active" />
  119. <el-option label="停用" value="inactive" />
  120. </el-select>
  121. </el-form-item>
  122. </el-form>
  123. <div slot="footer">
  124. <el-button @click="dialogVisible = false">取消</el-button>
  125. <el-button type="primary" @click="saveItem">保存</el-button>
  126. </div>
  127. </el-dialog>
  128. </div>
  129. </template>
  130. <script>
  131. import { getFoodDetail, saveFoodMonths } from '@/api/admin'
  132. export default {
  133. name: 'Foods',
  134. data() {
  135. return {
  136. loading: false,
  137. list: [],
  138. dialogVisible: false,
  139. categoryFilter: '',
  140. keyword: '',
  141. editId: null,
  142. form: {
  143. name: '',
  144. category: '',
  145. calories: 0,
  146. protein: 0,
  147. carbs: 0,
  148. fat: 0,
  149. fiber: 0,
  150. energyKj: 0,
  151. starch: 0,
  152. cholesterol: 0,
  153. unit: '克',
  154. status: 'active',
  155. months: []
  156. }
  157. }
  158. },
  159. created() {
  160. this.loadList()
  161. },
  162. methods: {
  163. async loadList() {
  164. this.loading = true
  165. try {
  166. const baseURL = process.env.VUE_APP_BASE_API || ''
  167. const token = localStorage.getItem('token')
  168. const axios = this.$axios || (await import('axios')).default
  169. const res = await axios.post(baseURL + '/api/admin/foods/list', { category: this.categoryFilter || undefined, keyword: this.keyword || undefined }, {
  170. headers: { Authorization: 'Bearer ' + token }
  171. })
  172. this.list = res.data && res.data.data || []
  173. } catch (e) {
  174. this.$message && this.$message.error && this.$message.error('加载失败')
  175. } finally {
  176. this.loading = false
  177. }
  178. },
  179. handleSearch() {
  180. this.page = 1
  181. this.loadList()
  182. },
  183. openCreate() {
  184. this.editId = null
  185. this.form = { name: '', category: '', calories: 0, protein: 0, carbs: 0, fat: 0, fiber: 0, energyKj: 0, starch: 0, cholesterol: 0, unit: '克', status: 'active', months: [] }
  186. this.dialogVisible = true
  187. },
  188. async editItem(row) {
  189. this.editId = row.id
  190. this.form = {
  191. name: row.name || '',
  192. category: row.category || '',
  193. calories: row.calories || 0,
  194. protein: row.protein || 0,
  195. carbs: row.carbs || 0,
  196. fat: row.fat || 0,
  197. fiber: row.fiber || 0,
  198. energyKj: row.energyKj || 0,
  199. starch: row.starch || 0,
  200. cholesterol: row.cholesterol || 0,
  201. unit: row.unit || '克',
  202. status: row.status || 'active',
  203. months: []
  204. }
  205. try {
  206. const res = await getFoodDetail(row.id)
  207. if (res.data && res.data.months) {
  208. this.form.months = res.data.months
  209. }
  210. } catch (e) {
  211. // months stay empty
  212. }
  213. this.dialogVisible = true
  214. },
  215. async saveItem() {
  216. try {
  217. const baseURL = process.env.VUE_APP_BASE_API || ''
  218. const token = localStorage.getItem('token')
  219. const axios = this.$axios || (await import('axios')).default
  220. const data = { ...this.form }
  221. const months = data.months
  222. delete data.months
  223. let foodId = this.editId
  224. if (foodId) {
  225. data.id = foodId
  226. await axios.post(baseURL + '/api/admin/foods/update', data, {
  227. headers: { Authorization: 'Bearer ' + token }
  228. })
  229. this.$message && this.$message.success && this.$message.success('更新成功')
  230. } else {
  231. const res = await axios.post(baseURL + '/api/admin/foods/create', data, {
  232. headers: { Authorization: 'Bearer ' + token }
  233. })
  234. foodId = res.data && res.data.data && res.data.data.id
  235. this.$message && this.$message.success && this.$message.success('创建成功')
  236. }
  237. // Save seasonal months
  238. if (foodId && months && months.length > 0) {
  239. try {
  240. await saveFoodMonths(foodId, months)
  241. } catch (e) {
  242. this.$message && this.$message.warning && this.$message.warning('应季月份保存失败')
  243. }
  244. }
  245. this.dialogVisible = false
  246. this.loadList()
  247. } catch (e) {
  248. this.$message && this.$message.error && this.$message.error('保存失败')
  249. }
  250. },
  251. async deleteItem(row) {
  252. this.$confirm('确定删除 "' + row.name + '" 吗?', '提示', { type: 'warning' }).then(async () => {
  253. try {
  254. const baseURL = process.env.VUE_APP_BASE_API || ''
  255. const token = localStorage.getItem('token')
  256. const axios = this.$axios || (await import('axios')).default
  257. await axios.post(baseURL + '/api/admin/foods/delete', { id: row.id }, {
  258. headers: { Authorization: 'Bearer ' + token }
  259. })
  260. this.$message && this.$message.success && this.$message.success('删除成功')
  261. this.loadList()
  262. } catch (e) {
  263. this.$message && this.$message.error && this.$message.error('删除失败')
  264. }
  265. }).catch(() => {})
  266. },
  267. handleActionCmd(row, cmd) {
  268. switch (cmd) {
  269. case 'delete':
  270. this.deleteItem(row)
  271. break
  272. }
  273. }
  274. }
  275. }
  276. </script>