配方管理界面
This commit is contained in:
40
src/components/feed/RecipeTable.vue
Normal file
40
src/components/feed/RecipeTable.vue
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<template>
|
||||||
|
<el-table
|
||||||
|
:data="recipes"
|
||||||
|
style="width: 100%"
|
||||||
|
:fit="true"
|
||||||
|
table-layout="auto"
|
||||||
|
row-key="id"
|
||||||
|
:highlight-current-row="false"
|
||||||
|
:scrollbar-always-on="true"
|
||||||
|
>
|
||||||
|
<el-table-column prop="id" label="ID" min-width="80" />
|
||||||
|
<el-table-column prop="name" label="配方名" min-width="150" />
|
||||||
|
<el-table-column label="原料种类数" min-width="120">
|
||||||
|
<template #default="scope">
|
||||||
|
{{ scope.row.recipe_ingredients ? scope.row.recipe_ingredients.length : 0 }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="description" label="配方简介" min-width="200" />
|
||||||
|
<el-table-column label="操作" min-width="150" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button size="small" @click="$emit('edit', scope.row)">编辑</el-button>
|
||||||
|
<el-button size="small" type="danger" @click="$emit('delete', scope.row)">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'RecipeTable',
|
||||||
|
props: {
|
||||||
|
recipes: {
|
||||||
|
type: Array,
|
||||||
|
required: true,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
emits: ['edit', 'delete']
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -109,6 +109,12 @@
|
|||||||
</el-icon>
|
</el-icon>
|
||||||
<template #title>品种管理</template>
|
<template #title>品种管理</template>
|
||||||
</el-menu-item>
|
</el-menu-item>
|
||||||
|
<el-menu-item index="/feed/recipes">
|
||||||
|
<el-icon>
|
||||||
|
<Tickets/>
|
||||||
|
</el-icon>
|
||||||
|
<template #title>配方管理</template>
|
||||||
|
</el-menu-item>
|
||||||
|
|
||||||
</el-sub-menu>
|
</el-sub-menu>
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ import RawMaterialList from '../views/feed/RawMaterialList.vue';
|
|||||||
import NutrientList from '../views/feed/NutrientList.vue'; // 修正拼写错误
|
import NutrientList from '../views/feed/NutrientList.vue'; // 修正拼写错误
|
||||||
import PigAgeStageList from '../views/feed/PigAgeStageList.vue'; // 导入 PigAgeStageList 组件
|
import PigAgeStageList from '../views/feed/PigAgeStageList.vue'; // 导入 PigAgeStageList 组件
|
||||||
import PigBreedList from '../views/feed/PigBreedList.vue'; // 导入 PigBreedList 组件
|
import PigBreedList from '../views/feed/PigBreedList.vue'; // 导入 PigBreedList 组件
|
||||||
|
import RecipeList from '../views/feed/RecipeList.vue';
|
||||||
|
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{path: '/', component: Home, meta: {requiresAuth: true, title: '系统首页'}},
|
{path: '/', component: Home, meta: {requiresAuth: true, title: '系统首页'}},
|
||||||
@@ -42,6 +44,8 @@ const routes = [
|
|||||||
{path: '/feed/nutrients', component: NutrientList, meta: {requiresAuth: true, title: '营养管理'}},
|
{path: '/feed/nutrients', component: NutrientList, meta: {requiresAuth: true, title: '营养管理'}},
|
||||||
{path: '/feed/pig-age-stages', component: PigAgeStageList, meta: {requiresAuth: true, title: '年龄阶段管理'}}, // 添加年龄阶段管理路由
|
{path: '/feed/pig-age-stages', component: PigAgeStageList, meta: {requiresAuth: true, title: '年龄阶段管理'}}, // 添加年龄阶段管理路由
|
||||||
{path: '/feed/pig-breeds', component: PigBreedList, meta: {requiresAuth: true, title: '品种管理'}}, // 添加品种管理路由
|
{path: '/feed/pig-breeds', component: PigBreedList, meta: {requiresAuth: true, title: '品种管理'}}, // 添加品种管理路由
|
||||||
|
{path: '/feed/recipes', component: RecipeList, meta: {requiresAuth: true, title: '配方管理'}},
|
||||||
|
|
||||||
{path: '/monitor/device-command-logs', component: DeviceCommandLogView, meta: {requiresAuth: true, title: '设备命令日志'}},
|
{path: '/monitor/device-command-logs', component: DeviceCommandLogView, meta: {requiresAuth: true, title: '设备命令日志'}},
|
||||||
{path: '/monitor/medication-logs', component: MedicationLogsView, meta: {requiresAuth: true, title: '用药记录'}},
|
{path: '/monitor/medication-logs', component: MedicationLogsView, meta: {requiresAuth: true, title: '用药记录'}},
|
||||||
{path: '/monitor/notifications', component: NotificationLogView, meta: {requiresAuth: true, title: '通知记录'}},
|
{path: '/monitor/notifications', component: NotificationLogView, meta: {requiresAuth: true, title: '通知记录'}},
|
||||||
|
|||||||
166
src/views/feed/RecipeList.vue
Normal file
166
src/views/feed/RecipeList.vue
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
<template>
|
||||||
|
<div class="recipe-list">
|
||||||
|
<el-card>
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<div class="title-container">
|
||||||
|
<h2 class="page-title">配方管理</h2>
|
||||||
|
<el-button type="text" @click="loadRecipes" class="refresh-btn" title="刷新配方列表">
|
||||||
|
<el-icon :size="20"><Refresh /></el-icon>
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<el-button type="primary" @click="addRecipe">新增配方</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 加载状态 -->
|
||||||
|
<div v-if="loading" class="loading">
|
||||||
|
<el-skeleton animated />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 错误状态 -->
|
||||||
|
<div v-else-if="error" class="error">
|
||||||
|
<el-alert
|
||||||
|
title="获取配方数据失败"
|
||||||
|
:description="error"
|
||||||
|
type="error"
|
||||||
|
show-icon
|
||||||
|
closable
|
||||||
|
@close="error = null"
|
||||||
|
/>
|
||||||
|
<el-button type="primary" @click="loadRecipes" class="retry-btn">重新加载</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 配方列表 -->
|
||||||
|
<RecipeTable
|
||||||
|
v-else
|
||||||
|
:recipes="recipes"
|
||||||
|
@edit="editRecipe"
|
||||||
|
@delete="deleteRecipe"
|
||||||
|
/>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { Refresh } from '@element-plus/icons-vue';
|
||||||
|
import { getRecipes, deleteRecipe as deleteRecipeApi } from '../../api/feed.js';
|
||||||
|
import RecipeTable from '../../components/feed/RecipeTable.vue';
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'RecipeList',
|
||||||
|
components: {
|
||||||
|
RecipeTable,
|
||||||
|
Refresh
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
recipes: [],
|
||||||
|
loading: false,
|
||||||
|
error: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
async mounted() {
|
||||||
|
await this.loadRecipes();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async loadRecipes() {
|
||||||
|
this.loading = true;
|
||||||
|
this.error = null;
|
||||||
|
try {
|
||||||
|
const response = await getRecipes();
|
||||||
|
this.recipes = response.data.list || [];
|
||||||
|
} catch (err) {
|
||||||
|
this.error = err.message || '未知错误';
|
||||||
|
console.error('加载配方列表失败:', err);
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
addRecipe() {
|
||||||
|
console.log('addRecipe triggered');
|
||||||
|
// 后续将实现表单对话框
|
||||||
|
ElMessage.info('新增功能待实现');
|
||||||
|
},
|
||||||
|
editRecipe(recipe) {
|
||||||
|
console.log('editRecipe triggered for:', recipe);
|
||||||
|
// 后续将实现表单对话框
|
||||||
|
ElMessage.info('编辑功能待实现');
|
||||||
|
},
|
||||||
|
async deleteRecipe(recipe) {
|
||||||
|
try {
|
||||||
|
await ElMessageBox.confirm(
|
||||||
|
`确认删除配方 "${recipe.name}" 吗?`,
|
||||||
|
'提示',
|
||||||
|
{
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
await deleteRecipeApi(recipe.id);
|
||||||
|
ElMessage.success('删除成功');
|
||||||
|
await this.loadRecipes();
|
||||||
|
} catch (err) {
|
||||||
|
if (err !== 'cancel') {
|
||||||
|
ElMessage.error('删除失败: ' + (err.message || '未知错误'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.recipe-list {
|
||||||
|
padding: 20px;
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 15px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.refresh-btn {
|
||||||
|
color: black;
|
||||||
|
background-color: transparent;
|
||||||
|
padding: 0;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading {
|
||||||
|
padding: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
padding: 20px 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.retry-btn {
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user