Compare commits
	
		
			2 Commits
		
	
	
		
			9944340d17
			...
			cc7ea94e41
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| cc7ea94e41 | |||
| 40a19b831a | 
							
								
								
									
										21
									
								
								frontend/dist/assets/index.0581bd6d.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								frontend/dist/assets/index.0581bd6d.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										21
									
								
								frontend/dist/assets/index.40048162.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										21
									
								
								frontend/dist/assets/index.40048162.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										1
									
								
								frontend/dist/assets/index.42c8d2d4.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								frontend/dist/assets/index.42c8d2d4.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										1
									
								
								frontend/dist/assets/index.4965a25a.css
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								frontend/dist/assets/index.4965a25a.css
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										4
									
								
								frontend/dist/index.html
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								frontend/dist/index.html
									
									
									
									
										vendored
									
									
								
							@@ -4,8 +4,8 @@
 | 
				
			|||||||
    <meta charset="UTF-8">
 | 
					    <meta charset="UTF-8">
 | 
				
			||||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | 
					    <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | 
				
			||||||
    <title>猪场管理系统</title>
 | 
					    <title>猪场管理系统</title>
 | 
				
			||||||
  <script type="module" crossorigin src="/assets/index.40048162.js"></script>
 | 
					  <script type="module" crossorigin src="/assets/index.0581bd6d.js"></script>
 | 
				
			||||||
  <link rel="stylesheet" href="/assets/index.4965a25a.css">
 | 
					  <link rel="stylesheet" href="/assets/index.42c8d2d4.css">
 | 
				
			||||||
</head>
 | 
					</head>
 | 
				
			||||||
<body>
 | 
					<body>
 | 
				
			||||||
    <div id="app"></div>
 | 
					    <div id="app"></div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,6 +12,7 @@
 | 
				
			|||||||
      <ul>
 | 
					      <ul>
 | 
				
			||||||
        <li><router-link to="/dashboard" class="active">控制台</router-link></li>
 | 
					        <li><router-link to="/dashboard" class="active">控制台</router-link></li>
 | 
				
			||||||
        <li><router-link to="/device">设备管理</router-link></li>
 | 
					        <li><router-link to="/device">设备管理</router-link></li>
 | 
				
			||||||
 | 
					        <li><router-link to="/feed/plan">饲喂计划</router-link></li>
 | 
				
			||||||
      </ul>
 | 
					      </ul>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,6 +8,14 @@
 | 
				
			|||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </header>
 | 
					    </header>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <nav class="nav">
 | 
				
			||||||
 | 
					      <ul>
 | 
				
			||||||
 | 
					        <li><router-link to="/dashboard">控制台</router-link></li>
 | 
				
			||||||
 | 
					        <li><router-link to="/device" class="active">设备管理</router-link></li>
 | 
				
			||||||
 | 
					        <li><router-link to="/feed/plan">饲喂计划</router-link></li>
 | 
				
			||||||
 | 
					      </ul>
 | 
				
			||||||
 | 
					    </nav>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <main class="main-content">
 | 
					    <main class="main-content">
 | 
				
			||||||
      <div class="toolbar">
 | 
					      <div class="toolbar">
 | 
				
			||||||
        <button class="btn btn-primary" @click="openAddDeviceModal">添加设备</button>
 | 
					        <button class="btn btn-primary" @click="openAddDeviceModal">添加设备</button>
 | 
				
			||||||
@@ -481,6 +489,39 @@ export default {
 | 
				
			|||||||
  background-color: #f5f7fa;
 | 
					  background-color: #f5f7fa;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nav {
 | 
				
			||||||
 | 
					  background-color: #343a40;
 | 
				
			||||||
 | 
					  padding: 0;
 | 
				
			||||||
 | 
					  margin-bottom: 20px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nav ul {
 | 
				
			||||||
 | 
					  list-style-type: none;
 | 
				
			||||||
 | 
					  margin: 0;
 | 
				
			||||||
 | 
					  padding: 0;
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nav li {
 | 
				
			||||||
 | 
					  margin: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nav a {
 | 
				
			||||||
 | 
					  display: block;
 | 
				
			||||||
 | 
					  padding: 15px 20px;
 | 
				
			||||||
 | 
					  color: #fff;
 | 
				
			||||||
 | 
					  text-decoration: none;
 | 
				
			||||||
 | 
					  transition: background-color 0.3s;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nav a:hover {
 | 
				
			||||||
 | 
					  background-color: #495057;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nav a.active {
 | 
				
			||||||
 | 
					  background-color: #007bff;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.header {
 | 
					.header {
 | 
				
			||||||
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
 | 
					  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
 | 
				
			||||||
  color: white;
 | 
					  color: white;
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										403
									
								
								frontend/src/pages/FeedPlan.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										403
									
								
								frontend/src/pages/FeedPlan.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,403 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					  <div class="feed-plan-management">
 | 
				
			||||||
 | 
					    <div class="header">
 | 
				
			||||||
 | 
					      <h1>饲喂计划管理</h1>
 | 
				
			||||||
 | 
					      <div class="user-info">
 | 
				
			||||||
 | 
					        <span>欢迎, {{ username }}</span>
 | 
				
			||||||
 | 
					        <button class="logout-btn" @click="logout">退出</button>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <nav class="nav">
 | 
				
			||||||
 | 
					      <ul>
 | 
				
			||||||
 | 
					        <li><router-link to="/dashboard">控制台</router-link></li>
 | 
				
			||||||
 | 
					        <li><router-link to="/device">设备管理</router-link></li>
 | 
				
			||||||
 | 
					        <li><router-link to="/feed/plan" class="active">饲喂计划</router-link></li>
 | 
				
			||||||
 | 
					      </ul>
 | 
				
			||||||
 | 
					    </nav>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <main class="main-content">
 | 
				
			||||||
 | 
					      <div class="toolbar">
 | 
				
			||||||
 | 
					        <button class="btn btn-primary" @click="createPlan">创建计划</button>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      <div class="plan-list">
 | 
				
			||||||
 | 
					        <div v-if="loading" class="loading">
 | 
				
			||||||
 | 
					          加载中...
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        <div v-else-if="plans.length === 0" class="no-plans">
 | 
				
			||||||
 | 
					          暂无饲喂计划
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        <div v-else class="plans-container">
 | 
				
			||||||
 | 
					          <div 
 | 
				
			||||||
 | 
					            v-for="plan in plans" 
 | 
				
			||||||
 | 
					            :key="plan.id" 
 | 
				
			||||||
 | 
					            class="plan-card"
 | 
				
			||||||
 | 
					          >
 | 
				
			||||||
 | 
					            <div class="plan-header">
 | 
				
			||||||
 | 
					              <h3>{{ plan.name }}</h3>
 | 
				
			||||||
 | 
					              <span :class="['plan-status', { 'enabled': plan.enabled, 'disabled': !plan.enabled }]">
 | 
				
			||||||
 | 
					                {{ plan.enabled ? '已启用' : '已禁用' }}
 | 
				
			||||||
 | 
					              </span>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            <div class="plan-details">
 | 
				
			||||||
 | 
					              <p class="plan-description">{{ plan.description || '暂无描述' }}</p>
 | 
				
			||||||
 | 
					              <div class="plan-meta">
 | 
				
			||||||
 | 
					                <span class="plan-type">{{ plan.type === 'manual' ? '手动触发' : '自动触发' }}</span>
 | 
				
			||||||
 | 
					                <span v-if="plan.schedule_cron" class="plan-cron">定时: {{ plan.schedule_cron }}</span>
 | 
				
			||||||
 | 
					              </div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            <div class="plan-actions">
 | 
				
			||||||
 | 
					              <button class="action-btn detail-btn" @click="viewDetail(plan.id)">详情</button>
 | 
				
			||||||
 | 
					              <button class="action-btn edit-btn" @click="editPlan(plan)">编辑</button>
 | 
				
			||||||
 | 
					              <button class="action-btn delete-btn" @click="deletePlan(plan.id)">删除</button>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </main>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script>
 | 
				
			||||||
 | 
					export default {
 | 
				
			||||||
 | 
					  name: 'FeedPlan',
 | 
				
			||||||
 | 
					  data() {
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      username: '',
 | 
				
			||||||
 | 
					      plans: [],
 | 
				
			||||||
 | 
					      loading: true
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  mounted() {
 | 
				
			||||||
 | 
					    this.username = localStorage.getItem('username') || '管理员'
 | 
				
			||||||
 | 
					    this.loadPlans()
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  methods: {
 | 
				
			||||||
 | 
					    // 加载饲喂计划列表
 | 
				
			||||||
 | 
					    async loadPlans() {
 | 
				
			||||||
 | 
					      this.loading = true
 | 
				
			||||||
 | 
					      try {
 | 
				
			||||||
 | 
					        const response = await fetch('/api/v1/feed/plan/list', {
 | 
				
			||||||
 | 
					          method: 'GET',
 | 
				
			||||||
 | 
					          headers: {
 | 
				
			||||||
 | 
					            'Content-Type': 'application/json',
 | 
				
			||||||
 | 
					            'Authorization': 'Bearer ' + localStorage.getItem('authToken')
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        const data = await response.json()
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if (response.ok && data.code === 0) {
 | 
				
			||||||
 | 
					          this.plans = data.data.plans || []
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          console.error('获取饲喂计划列表失败:', data.message)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      } catch (error) {
 | 
				
			||||||
 | 
					        console.error('获取饲喂计划列表失败:', error)
 | 
				
			||||||
 | 
					      } finally {
 | 
				
			||||||
 | 
					        this.loading = false
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // 查看详情
 | 
				
			||||||
 | 
					    viewDetail(planId) {
 | 
				
			||||||
 | 
					      this.$router.push(`/feed/plan/${planId}`)
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // 创建计划
 | 
				
			||||||
 | 
					    createPlan() {
 | 
				
			||||||
 | 
					      // TODO: 实现创建计划逻辑
 | 
				
			||||||
 | 
					      alert('创建计划功能待实现')
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // 编辑计划
 | 
				
			||||||
 | 
					    editPlan(plan) {
 | 
				
			||||||
 | 
					      // TODO: 实现编辑计划逻辑
 | 
				
			||||||
 | 
					      alert(`编辑计划: ${plan.name}`)
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // 删除计划
 | 
				
			||||||
 | 
					    async deletePlan(planId) {
 | 
				
			||||||
 | 
					      if (!confirm('确定要删除这个饲喂计划吗?')) {
 | 
				
			||||||
 | 
					        return
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      try {
 | 
				
			||||||
 | 
					        const response = await fetch('/api/v1/feed/plan/delete', {
 | 
				
			||||||
 | 
					          method: 'POST',
 | 
				
			||||||
 | 
					          headers: {
 | 
				
			||||||
 | 
					            'Content-Type': 'application/json',
 | 
				
			||||||
 | 
					            'Authorization': 'Bearer ' + localStorage.getItem('authToken')
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          body: JSON.stringify({ id: planId })
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        const data = await response.json()
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if (response.ok && data.code === 0) {
 | 
				
			||||||
 | 
					          // 删除成功,重新加载列表
 | 
				
			||||||
 | 
					          await this.loadPlans()
 | 
				
			||||||
 | 
					          this.$message?.success('删除成功') || alert('删除成功')
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          this.$message?.error('删除失败: ' + data.message) || alert('删除失败: ' + data.message)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      } catch (error) {
 | 
				
			||||||
 | 
					        console.error('删除饲喂计划失败:', error)
 | 
				
			||||||
 | 
					        this.$message?.error('删除饲喂计划失败: ' + error.message) || alert('删除饲喂计划失败: ' + error.message)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // 退出登录
 | 
				
			||||||
 | 
					    logout() {
 | 
				
			||||||
 | 
					      localStorage.removeItem('authToken')
 | 
				
			||||||
 | 
					      localStorage.removeItem('username')
 | 
				
			||||||
 | 
					      this.$router.push('/')
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style scoped>
 | 
				
			||||||
 | 
					.feed-plan-management {
 | 
				
			||||||
 | 
					  padding: 20px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.header {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  justify-content: space-between;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  margin-bottom: 30px;
 | 
				
			||||||
 | 
					  padding-bottom: 20px;
 | 
				
			||||||
 | 
					  border-bottom: 1px solid #eee;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.header h1 {
 | 
				
			||||||
 | 
					  margin: 0;
 | 
				
			||||||
 | 
					  color: #333;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.user-info {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  gap: 15px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.logout-btn {
 | 
				
			||||||
 | 
					  padding: 8px 16px;
 | 
				
			||||||
 | 
					  background-color: #dc3545;
 | 
				
			||||||
 | 
					  color: white;
 | 
				
			||||||
 | 
					  border: none;
 | 
				
			||||||
 | 
					  border-radius: 4px;
 | 
				
			||||||
 | 
					  cursor: pointer;
 | 
				
			||||||
 | 
					  transition: background-color 0.3s;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.logout-btn:hover {
 | 
				
			||||||
 | 
					  background-color: #c82333;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nav {
 | 
				
			||||||
 | 
					  background-color: #343a40;
 | 
				
			||||||
 | 
					  padding: 0;
 | 
				
			||||||
 | 
					  margin-bottom: 20px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nav ul {
 | 
				
			||||||
 | 
					  list-style-type: none;
 | 
				
			||||||
 | 
					  margin: 0;
 | 
				
			||||||
 | 
					  padding: 0;
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nav li {
 | 
				
			||||||
 | 
					  margin: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nav a {
 | 
				
			||||||
 | 
					  display: block;
 | 
				
			||||||
 | 
					  padding: 15px 20px;
 | 
				
			||||||
 | 
					  color: #fff;
 | 
				
			||||||
 | 
					  text-decoration: none;
 | 
				
			||||||
 | 
					  transition: background-color 0.3s;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nav a:hover {
 | 
				
			||||||
 | 
					  background-color: #495057;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nav a.active {
 | 
				
			||||||
 | 
					  background-color: #007bff;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.toolbar {
 | 
				
			||||||
 | 
					  margin-bottom: 20px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.btn {
 | 
				
			||||||
 | 
					  padding: 10px 20px;
 | 
				
			||||||
 | 
					  border: none;
 | 
				
			||||||
 | 
					  border-radius: 4px;
 | 
				
			||||||
 | 
					  cursor: pointer;
 | 
				
			||||||
 | 
					  font-size: 14px;
 | 
				
			||||||
 | 
					  transition: background-color 0.3s;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.btn-primary {
 | 
				
			||||||
 | 
					  background-color: #007bff;
 | 
				
			||||||
 | 
					  color: white;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.btn-primary:hover {
 | 
				
			||||||
 | 
					  background-color: #0069d9;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.plan-list {
 | 
				
			||||||
 | 
					  min-height: 400px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.loading, .no-plans {
 | 
				
			||||||
 | 
					  text-align: center;
 | 
				
			||||||
 | 
					  padding: 50px;
 | 
				
			||||||
 | 
					  color: #666;
 | 
				
			||||||
 | 
					  font-size: 16px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.plans-container {
 | 
				
			||||||
 | 
					  display: grid;
 | 
				
			||||||
 | 
					  grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
 | 
				
			||||||
 | 
					  gap: 20px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.plan-card {
 | 
				
			||||||
 | 
					  border: 1px solid #ddd;
 | 
				
			||||||
 | 
					  border-radius: 8px;
 | 
				
			||||||
 | 
					  padding: 20px;
 | 
				
			||||||
 | 
					  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
 | 
				
			||||||
 | 
					  background-color: white;
 | 
				
			||||||
 | 
					  transition: box-shadow 0.3s, transform 0.3s;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.plan-card:hover {
 | 
				
			||||||
 | 
					  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
 | 
				
			||||||
 | 
					  transform: translateY(-2px);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.plan-header {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  justify-content: space-between;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  margin-bottom: 15px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.plan-header h3 {
 | 
				
			||||||
 | 
					  margin: 0;
 | 
				
			||||||
 | 
					  color: #333;
 | 
				
			||||||
 | 
					  font-size: 18px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.plan-status {
 | 
				
			||||||
 | 
					  padding: 4px 8px;
 | 
				
			||||||
 | 
					  border-radius: 4px;
 | 
				
			||||||
 | 
					  font-size: 12px;
 | 
				
			||||||
 | 
					  font-weight: bold;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.plan-status.enabled {
 | 
				
			||||||
 | 
					  background-color: #d4edda;
 | 
				
			||||||
 | 
					  color: #155724;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.plan-status.disabled {
 | 
				
			||||||
 | 
					  background-color: #f8d7da;
 | 
				
			||||||
 | 
					  color: #721c24;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.plan-details {
 | 
				
			||||||
 | 
					  margin-bottom: 20px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.plan-description {
 | 
				
			||||||
 | 
					  margin: 0 0 15px 0;
 | 
				
			||||||
 | 
					  color: #666;
 | 
				
			||||||
 | 
					  line-height: 1.5;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.plan-meta {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  flex-wrap: wrap;
 | 
				
			||||||
 | 
					  gap: 10px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.plan-type, .plan-cron {
 | 
				
			||||||
 | 
					  padding: 4px 8px;
 | 
				
			||||||
 | 
					  background-color: #e9ecef;
 | 
				
			||||||
 | 
					  border-radius: 4px;
 | 
				
			||||||
 | 
					  font-size: 12px;
 | 
				
			||||||
 | 
					  color: #495057;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.plan-actions {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  gap: 10px;
 | 
				
			||||||
 | 
					  justify-content: flex-end;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.action-btn {
 | 
				
			||||||
 | 
					  padding: 6px 12px;
 | 
				
			||||||
 | 
					  border: none;
 | 
				
			||||||
 | 
					  border-radius: 4px;
 | 
				
			||||||
 | 
					  cursor: pointer;
 | 
				
			||||||
 | 
					  font-size: 13px;
 | 
				
			||||||
 | 
					  transition: background-color 0.3s;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.detail-btn {
 | 
				
			||||||
 | 
					  background-color: #17a2b8;
 | 
				
			||||||
 | 
					  color: white;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.detail-btn:hover {
 | 
				
			||||||
 | 
					  background-color: #138496;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.edit-btn {
 | 
				
			||||||
 | 
					  background-color: #ffc107;
 | 
				
			||||||
 | 
					  color: #212529;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.edit-btn:hover {
 | 
				
			||||||
 | 
					  background-color: #e0a800;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.delete-btn {
 | 
				
			||||||
 | 
					  background-color: #dc3545;
 | 
				
			||||||
 | 
					  color: white;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.delete-btn:hover {
 | 
				
			||||||
 | 
					  background-color: #c82333;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@media (max-width: 768px) {
 | 
				
			||||||
 | 
					  .plans-container {
 | 
				
			||||||
 | 
					    grid-template-columns: 1fr;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  .header {
 | 
				
			||||||
 | 
					    flex-direction: column;
 | 
				
			||||||
 | 
					    gap: 15px;
 | 
				
			||||||
 | 
					    align-items: flex-start;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  .nav ul {
 | 
				
			||||||
 | 
					    flex-direction: column;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
@@ -2,6 +2,7 @@ import { createRouter, createWebHistory } from 'vue-router'
 | 
				
			|||||||
import Login from '../pages/Login.vue'
 | 
					import Login from '../pages/Login.vue'
 | 
				
			||||||
import Dashboard from '../pages/Dashboard.vue'
 | 
					import Dashboard from '../pages/Dashboard.vue'
 | 
				
			||||||
import Device from '../pages/Device.vue'
 | 
					import Device from '../pages/Device.vue'
 | 
				
			||||||
 | 
					import FeedPlan from '../pages/FeedPlan.vue'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const routes = [
 | 
					const routes = [
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
@@ -20,6 +21,12 @@ const routes = [
 | 
				
			|||||||
    name: 'Device',
 | 
					    name: 'Device',
 | 
				
			||||||
    component: Device,
 | 
					    component: Device,
 | 
				
			||||||
    meta: { requiresAuth: true }
 | 
					    meta: { requiresAuth: true }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    path: '/feed/plan',
 | 
				
			||||||
 | 
					    name: 'FeedPlan',
 | 
				
			||||||
 | 
					    component: FeedPlan,
 | 
				
			||||||
 | 
					    meta: { requiresAuth: true }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -126,15 +126,17 @@ func (c *Controller) convertToCreateModel(req *CreateRequest) *model.FeedingPlan
 | 
				
			|||||||
// Delete 删除饲料计划
 | 
					// Delete 删除饲料计划
 | 
				
			||||||
func (c *Controller) Delete(ctx *gin.Context) {
 | 
					func (c *Controller) Delete(ctx *gin.Context) {
 | 
				
			||||||
	// 获取路径参数中的计划ID
 | 
						// 获取路径参数中的计划ID
 | 
				
			||||||
	planIDStr := ctx.Param("id")
 | 
						var req struct {
 | 
				
			||||||
	planID, err := strconv.ParseUint(planIDStr, 10, 32)
 | 
							ID uint `json:"id" binding:"required"`
 | 
				
			||||||
	if err != nil {
 | 
						}
 | 
				
			||||||
		controller.SendErrorResponse(ctx, controller.InvalidParameterCode, "无效的计划ID")
 | 
					
 | 
				
			||||||
 | 
						if err := ctx.ShouldBindJSON(&req); err != nil {
 | 
				
			||||||
 | 
							controller.SendErrorResponse(ctx, controller.InvalidParameterCode, "请求参数错误: "+err.Error())
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 调用仓库删除计划
 | 
						// 调用仓库删除计划
 | 
				
			||||||
	if err := c.feedPlanRepo.DeleteFeedingPlan(uint(planID)); err != nil {
 | 
						if err := c.feedPlanRepo.DeleteFeedingPlan(uint(req.ID)); err != nil {
 | 
				
			||||||
		c.logger.Error("删除计划失败: " + err.Error())
 | 
							c.logger.Error("删除计划失败: " + err.Error())
 | 
				
			||||||
		controller.SendErrorResponse(ctx, controller.InternalServerErrorCode, "删除计划失败")
 | 
							controller.SendErrorResponse(ctx, controller.InternalServerErrorCode, "删除计划失败")
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,6 +19,10 @@ var migrateModels = []interface{}{
 | 
				
			|||||||
	&model.OperationHistory{},
 | 
						&model.OperationHistory{},
 | 
				
			||||||
	&model.Device{},
 | 
						&model.Device{},
 | 
				
			||||||
	&model.DeviceControl{},
 | 
						&model.DeviceControl{},
 | 
				
			||||||
 | 
						&model.FeedingPlan{},
 | 
				
			||||||
 | 
						&model.FeedingPlanStep{},
 | 
				
			||||||
 | 
						&model.FeedingExecution{},
 | 
				
			||||||
 | 
						&model.FeedingExecutionStep{},
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// PostgresStorage 代表基于PostgreSQL的存储实现
 | 
					// PostgresStorage 代表基于PostgreSQL的存储实现
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user