增加饲喂计划列表展示界面
This commit is contained in:
1
frontend/dist/assets/index.398ac6b2.css
vendored
Normal file
1
frontend/dist/assets/index.398ac6b2.css
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.4965a25a.css
vendored
1
frontend/dist/assets/index.4965a25a.css
vendored
File diff suppressed because one or more lines are too long
21
frontend/dist/assets/index.80d29e01.js
vendored
Normal file
21
frontend/dist/assets/index.80d29e01.js
vendored
Normal file
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 name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>猪场管理系统</title>
|
||||
<script type="module" crossorigin src="/assets/index.40048162.js"></script>
|
||||
<link rel="stylesheet" href="/assets/index.4965a25a.css">
|
||||
<script type="module" crossorigin src="/assets/index.80d29e01.js"></script>
|
||||
<link rel="stylesheet" href="/assets/index.398ac6b2.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
<ul>
|
||||
<li><router-link to="/dashboard" class="active">控制台</router-link></li>
|
||||
<li><router-link to="/device">设备管理</router-link></li>
|
||||
<li><router-link to="/feed/plan">饲喂计划</router-link></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -8,6 +8,14 @@
|
||||
</div>
|
||||
</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">
|
||||
<div class="toolbar">
|
||||
<button class="btn btn-primary" @click="openAddDeviceModal">添加设备</button>
|
||||
@@ -481,6 +489,39 @@ export default {
|
||||
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 {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
|
||||
402
frontend/src/pages/FeedPlan.vue
Normal file
402
frontend/src/pages/FeedPlan.vue
Normal file
@@ -0,0 +1,402 @@
|
||||
<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/${planId}`, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer ' + localStorage.getItem('authToken')
|
||||
}
|
||||
})
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
if (response.ok && data.code === 0) {
|
||||
// 删除成功,重新加载列表
|
||||
await this.loadPlans()
|
||||
alert('删除成功')
|
||||
} else {
|
||||
alert('删除失败: ' + data.message)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('删除饲喂计划失败:', error)
|
||||
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 Dashboard from '../pages/Dashboard.vue'
|
||||
import Device from '../pages/Device.vue'
|
||||
import FeedPlan from '../pages/FeedPlan.vue'
|
||||
|
||||
const routes = [
|
||||
{
|
||||
@@ -20,6 +21,12 @@ const routes = [
|
||||
name: 'Device',
|
||||
component: Device,
|
||||
meta: { requiresAuth: true }
|
||||
},
|
||||
{
|
||||
path: '/feed/plan',
|
||||
name: 'FeedPlan',
|
||||
component: FeedPlan,
|
||||
meta: { requiresAuth: true }
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user