修改bug。新增牛只,超链接
This commit is contained in:
@@ -23,7 +23,7 @@
|
||||
宁夏智慧养殖监管平台
|
||||
</div>
|
||||
<div class="user-info">
|
||||
<a href="https://nxzhyz.xiyumuge.com/" target="_blank" style="color: white; text-decoration: none; margin-right: 16px;">大屏展示</a>
|
||||
<a href="https://datav.ningmuyun.com/" target="_blank" style="color: white; text-decoration: none; margin-right: 16px;">大屏展示</a>
|
||||
<span>欢迎, {{ userData?.username }}</span>
|
||||
<a-button type="link" @click="handleLogout" style="color: white;">退出</a-button>
|
||||
</div>
|
||||
|
||||
@@ -21,27 +21,15 @@
|
||||
<template v-for="subMenu in menu.children">
|
||||
<!-- 子菜单还有子菜单 -->
|
||||
<a-sub-menu v-if="subMenu.children && subMenu.children.length > 0" :key="subMenu.id">
|
||||
<template #icon>
|
||||
<component :is="subMenu.icon" v-if="subMenu.icon" />
|
||||
<SettingOutlined v-else />
|
||||
</template>
|
||||
<template #title>{{ subMenu.name }}</template>
|
||||
|
||||
<a-menu-item v-for="item in subMenu.children" :key="item.id">
|
||||
<template #icon>
|
||||
<component :is="item.icon" v-if="item.icon" />
|
||||
<FileOutlined v-else />
|
||||
</template>
|
||||
{{ item.name }}
|
||||
</a-menu-item>
|
||||
</a-sub-menu>
|
||||
|
||||
<!-- 子菜单是叶子节点 -->
|
||||
<a-menu-item v-else :key="subMenu.id">
|
||||
<template #icon>
|
||||
<component :is="subMenu.icon" v-if="subMenu.icon" />
|
||||
<FileOutlined v-else />
|
||||
</template>
|
||||
{{ subMenu.name }}
|
||||
</a-menu-item>
|
||||
</template>
|
||||
@@ -60,7 +48,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, watch } from 'vue'
|
||||
import { ref, onMounted, watch, nextTick } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import {
|
||||
HomeOutlined,
|
||||
@@ -101,6 +89,11 @@ const selectedKeys = ref([])
|
||||
const openKeys = ref([])
|
||||
const menuTree = ref([])
|
||||
|
||||
// 监听selectedKeys变化,用于调试
|
||||
watch(selectedKeys, (newKeys) => {
|
||||
console.log('selectedKeys变化:', newKeys)
|
||||
}, { deep: true })
|
||||
|
||||
// 图标映射
|
||||
const iconMap = {
|
||||
'DashboardOutlined': DashboardOutlined,
|
||||
@@ -195,8 +188,10 @@ const loadMenus = async () => {
|
||||
console.log('菜单树构建完成:', menuTree.value)
|
||||
console.log('菜单树长度:', menuTree.value.length)
|
||||
|
||||
// 设置当前选中的菜单项
|
||||
setActiveMenu()
|
||||
// 设置当前选中的菜单项,使用nextTick确保DOM更新完成
|
||||
nextTick(() => {
|
||||
setActiveMenu()
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('加载菜单数据失败:', error)
|
||||
message.error('加载菜单数据失败')
|
||||
@@ -273,11 +268,14 @@ const buildMenuTree = (menus) => {
|
||||
const setActiveMenu = () => {
|
||||
const currentPath = route.path
|
||||
console.log('当前路径:', currentPath)
|
||||
console.log('当前菜单树:', menuTree.value)
|
||||
|
||||
// 查找匹配的菜单项
|
||||
const findMenuByPath = (menus, path) => {
|
||||
for (const menu of menus) {
|
||||
console.log('检查菜单项:', menu.name, menu.path, '匹配路径:', path)
|
||||
if (menu.path === path) {
|
||||
console.log('找到匹配的菜单项:', menu.name, menu.id)
|
||||
return menu
|
||||
}
|
||||
if (menu.children) {
|
||||
@@ -289,12 +287,32 @@ const setActiveMenu = () => {
|
||||
}
|
||||
|
||||
const activeMenu = findMenuByPath(menuTree.value, currentPath)
|
||||
console.log('找到的激活菜单:', activeMenu)
|
||||
|
||||
if (activeMenu && activeMenu.id != null) {
|
||||
selectedKeys.value = [activeMenu.id.toString()]
|
||||
// 保持与模板中key绑定的数据类型一致(数字类型)
|
||||
const activeMenuId = activeMenu.id
|
||||
console.log('设置激活菜单项:', activeMenuId, '当前selectedKeys:', selectedKeys.value)
|
||||
|
||||
// 设置打开的父菜单
|
||||
// 强制更新选中状态,确保高亮显示
|
||||
selectedKeys.value = [activeMenuId]
|
||||
console.log('更新selectedKeys为:', selectedKeys.value)
|
||||
|
||||
// 强制触发响应式更新
|
||||
nextTick(() => {
|
||||
console.log('nextTick后selectedKeys:', selectedKeys.value)
|
||||
})
|
||||
|
||||
// 设置打开的父菜单,但保持已有的展开状态
|
||||
const parentIds = getParentMenuIds(activeMenu.id, menuTree.value)
|
||||
openKeys.value = parentIds ? parentIds.map(id => id != null ? id.toString() : '').filter(id => id) : []
|
||||
if (parentIds && parentIds.length > 0) {
|
||||
const parentKeys = parentIds.map(id => id != null ? id.toString() : '').filter(id => id)
|
||||
console.log('设置父级菜单展开:', parentKeys)
|
||||
// 合并已有的展开状态和新的父级菜单
|
||||
openKeys.value = [...new Set([...openKeys.value, ...parentKeys])]
|
||||
}
|
||||
} else {
|
||||
console.log('未找到匹配的菜单项,当前路径:', currentPath)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -316,6 +334,45 @@ const getParentMenuIds = (menuId, menus, parentIds = []) => {
|
||||
const handleOpenChange = (keys) => {
|
||||
console.log('菜单展开状态变化:', keys)
|
||||
openKeys.value = keys
|
||||
|
||||
// 当一级菜单展开时,自动选中第一个二级菜单项
|
||||
if (keys.length > 0) {
|
||||
const lastOpenedKey = keys[keys.length - 1]
|
||||
console.log('最后展开的菜单:', lastOpenedKey)
|
||||
|
||||
// 查找对应的一级菜单
|
||||
const findMenuById = (menus, id) => {
|
||||
for (const menu of menus) {
|
||||
if (menu && menu.id != null && menu.id.toString() === id.toString()) {
|
||||
return menu
|
||||
}
|
||||
if (menu && menu.children) {
|
||||
const found = findMenuById(menu.children, id)
|
||||
if (found) return found
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
const openedMenu = findMenuById(menuTree.value, lastOpenedKey)
|
||||
if (openedMenu && openedMenu.children && openedMenu.children.length > 0) {
|
||||
// 找到第一个二级菜单项
|
||||
const firstChild = openedMenu.children[0]
|
||||
if (firstChild && firstChild.id != null) {
|
||||
console.log('自动选中第一个二级菜单项:', firstChild.name, firstChild.id)
|
||||
selectedKeys.value = [firstChild.id]
|
||||
|
||||
// 如果第一个子菜单还有子菜单,继续查找
|
||||
if (firstChild.children && firstChild.children.length > 0) {
|
||||
const firstGrandChild = firstChild.children[0]
|
||||
if (firstGrandChild && firstGrandChild.id != null) {
|
||||
console.log('自动选中第一个三级菜单项:', firstGrandChild.name, firstGrandChild.id)
|
||||
selectedKeys.value = [firstGrandChild.id]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理菜单点击
|
||||
@@ -350,6 +407,24 @@ const handleMenuClick = ({ key }) => {
|
||||
const menuItem = findMenuById(menuTree.value, actualKey)
|
||||
if (menuItem && menuItem.path) {
|
||||
console.log('导航到:', menuItem.path)
|
||||
|
||||
// 确保父级菜单保持展开状态
|
||||
const parentIds = getParentMenuIds(menuItem.id, menuTree.value)
|
||||
if (parentIds && parentIds.length > 0) {
|
||||
const parentKeys = parentIds.map(id => id != null ? id.toString() : '').filter(id => id)
|
||||
console.log('保持父级菜单展开:', parentKeys)
|
||||
// 使用Set确保不重复,并保持原有展开状态
|
||||
const newOpenKeys = [...new Set([...openKeys.value, ...parentKeys])]
|
||||
openKeys.value = newOpenKeys
|
||||
}
|
||||
|
||||
// 先设置选中的菜单项,确保高亮显示
|
||||
// 将字符串转换为数字类型以匹配模板中的key绑定
|
||||
const menuId = parseInt(actualKey)
|
||||
selectedKeys.value = [menuId]
|
||||
console.log('设置选中菜单项:', menuId)
|
||||
|
||||
// 然后导航到对应路径
|
||||
router.push(menuItem.path)
|
||||
emit('menu-click', menuItem)
|
||||
} else {
|
||||
@@ -378,28 +453,24 @@ const getDefaultMenus = () => {
|
||||
id: 21,
|
||||
name: '智能耳标',
|
||||
path: '/smart-devices/eartag',
|
||||
icon: 'FileOutlined',
|
||||
children: []
|
||||
},
|
||||
{
|
||||
id: 22,
|
||||
name: '智能项圈',
|
||||
path: '/smart-devices/collar',
|
||||
icon: 'FileOutlined',
|
||||
children: []
|
||||
},
|
||||
{
|
||||
id: 23,
|
||||
name: '智能主机',
|
||||
path: '/smart-devices/host',
|
||||
icon: 'FileOutlined',
|
||||
children: []
|
||||
},
|
||||
{
|
||||
id: 24,
|
||||
name: '电子围栏',
|
||||
path: '/smart-devices/fence',
|
||||
icon: 'FileOutlined',
|
||||
children: []
|
||||
}
|
||||
]
|
||||
@@ -414,14 +485,12 @@ const getDefaultMenus = () => {
|
||||
id: 31,
|
||||
name: '智能耳标预警',
|
||||
path: '/smart-alerts/eartag',
|
||||
icon: 'FileOutlined',
|
||||
children: []
|
||||
},
|
||||
{
|
||||
id: 32,
|
||||
name: '智能项圈预警',
|
||||
path: '/smart-alerts/collar',
|
||||
icon: 'FileOutlined',
|
||||
children: []
|
||||
}
|
||||
]
|
||||
@@ -436,35 +505,30 @@ const getDefaultMenus = () => {
|
||||
id: 41,
|
||||
name: '牛只档案',
|
||||
path: '/cattle-management/archives',
|
||||
icon: 'FileOutlined',
|
||||
children: []
|
||||
},
|
||||
{
|
||||
id: 42,
|
||||
name: '栏舍设置',
|
||||
path: '/cattle-management/pens',
|
||||
icon: 'FileOutlined',
|
||||
children: []
|
||||
},
|
||||
{
|
||||
id: 43,
|
||||
name: '批次设置',
|
||||
path: '/cattle-management/batches',
|
||||
icon: 'FileOutlined',
|
||||
children: []
|
||||
},
|
||||
{
|
||||
id: 44,
|
||||
name: '转栏记录',
|
||||
path: '/cattle-management/transfer-records',
|
||||
icon: 'FileOutlined',
|
||||
children: []
|
||||
},
|
||||
{
|
||||
id: 45,
|
||||
name: '离栏记录',
|
||||
path: '/cattle-management/exit-records',
|
||||
icon: 'FileOutlined',
|
||||
children: []
|
||||
}
|
||||
]
|
||||
@@ -479,28 +543,24 @@ const getDefaultMenus = () => {
|
||||
id: 51,
|
||||
name: '养殖场信息管理',
|
||||
path: '/farm-management/info',
|
||||
icon: 'FileOutlined',
|
||||
children: []
|
||||
},
|
||||
{
|
||||
id: 52,
|
||||
name: '栏舍管理',
|
||||
path: '/farm-management/pens',
|
||||
icon: 'FileOutlined',
|
||||
children: []
|
||||
},
|
||||
{
|
||||
id: 53,
|
||||
name: '用户管理',
|
||||
path: '/farm-management/users',
|
||||
icon: 'FileOutlined',
|
||||
children: []
|
||||
},
|
||||
{
|
||||
id: 54,
|
||||
name: '角色权限管理',
|
||||
path: '/farm-management/role-permissions',
|
||||
icon: 'SafetyOutlined',
|
||||
children: []
|
||||
}
|
||||
]
|
||||
@@ -517,7 +577,12 @@ const getDefaultMenus = () => {
|
||||
|
||||
// 监听路由变化
|
||||
watch(() => route.path, () => {
|
||||
setActiveMenu()
|
||||
// 确保菜单树已加载完成后再设置激活状态
|
||||
if (menuTree.value && menuTree.value.length > 0) {
|
||||
nextTick(() => {
|
||||
setActiveMenu()
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// 监听用户数据变化,当用户数据加载完成后再加载菜单
|
||||
|
||||
@@ -158,13 +158,71 @@ watch(() => route.name, (newRouteName) => {
|
||||
)
|
||||
|
||||
if (currentRoute && !openKeys.value.includes(currentRoute.name)) {
|
||||
console.log('自动展开父级菜单:', currentRoute.name)
|
||||
openKeys.value = [...openKeys.value, currentRoute.name]
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
// 监听路由变化,确保父级菜单保持展开状态
|
||||
watch(() => route.path, (newPath) => {
|
||||
// 查找当前路径对应的父级菜单
|
||||
const findParentRoute = (routes, targetPath) => {
|
||||
for (const route of routes) {
|
||||
if (route.children) {
|
||||
for (const child of route.children) {
|
||||
if (child.path === targetPath) {
|
||||
return route
|
||||
}
|
||||
}
|
||||
// 递归查找更深层的子路由
|
||||
const found = findParentRoute(route.children, targetPath)
|
||||
if (found) return found
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
const parentRoute = findParentRoute(router.options.routes, newPath)
|
||||
if (parentRoute && !openKeys.value.includes(parentRoute.name)) {
|
||||
console.log('保持父级菜单展开:', parentRoute.name)
|
||||
openKeys.value = [...openKeys.value, parentRoute.name]
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
// 处理子菜单展开/收起
|
||||
const handleOpenChange = (keys) => {
|
||||
openKeys.value = keys
|
||||
|
||||
// 当一级菜单展开时,自动选中第一个二级菜单项
|
||||
if (keys.length > 0) {
|
||||
const lastOpenedKey = keys[keys.length - 1]
|
||||
console.log('最后展开的菜单:', lastOpenedKey)
|
||||
|
||||
// 查找对应的一级菜单
|
||||
const findRouteByName = (routes, name) => {
|
||||
for (const route of routes) {
|
||||
if (route.name === name) {
|
||||
return route
|
||||
}
|
||||
if (route.children) {
|
||||
const found = findRouteByName(route.children, name)
|
||||
if (found) return found
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
const openedRoute = findRouteByName(router.options.routes, lastOpenedKey)
|
||||
if (openedRoute && openedRoute.children && openedRoute.children.length > 0) {
|
||||
// 找到第一个二级菜单项
|
||||
const firstChild = openedRoute.children[0]
|
||||
if (firstChild && firstChild.name) {
|
||||
console.log('自动选中第一个二级菜单项:', firstChild.meta?.title, firstChild.name)
|
||||
// 导航到第一个子菜单
|
||||
router.push(firstChild.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 组件挂载时设置调试工具
|
||||
|
||||
@@ -129,32 +129,61 @@
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="8">
|
||||
<a-form-item label="品系" name="strain">
|
||||
<a-input
|
||||
<a-select
|
||||
v-model:value="formData.strain"
|
||||
placeholder="请输入品系"
|
||||
@input="handleFieldChange('strain', $event.target.value)"
|
||||
@change="handleFieldChange('strain', $event.target.value)"
|
||||
/>
|
||||
placeholder="请选择品系"
|
||||
:loading="cattleUsersLoading"
|
||||
@change="handleFieldChange('strain', $event)"
|
||||
show-search
|
||||
:filter-option="filterOption"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="user in cattleUsers"
|
||||
:key="user.id"
|
||||
:value="user.id"
|
||||
>
|
||||
{{ user.name }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="品种" name="varieties">
|
||||
<a-input
|
||||
<a-select
|
||||
v-model:value="formData.varieties"
|
||||
placeholder="请输入品种"
|
||||
@input="handleFieldChange('varieties', $event.target.value)"
|
||||
@change="handleFieldChange('varieties', $event.target.value)"
|
||||
/>
|
||||
placeholder="请选择品种"
|
||||
:loading="cattleTypesLoading"
|
||||
@change="handleFieldChange('varieties', $event)"
|
||||
show-search
|
||||
:filter-option="filterOption"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="type in cattleTypes"
|
||||
:key="type.id"
|
||||
:value="type.id"
|
||||
>
|
||||
{{ type.name }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="类别" name="cate">
|
||||
<a-input
|
||||
<a-select
|
||||
v-model:value="formData.cate"
|
||||
placeholder="请输入类别"
|
||||
@input="handleFieldChange('cate', $event.target.value)"
|
||||
@change="handleFieldChange('cate', $event.target.value)"
|
||||
/>
|
||||
placeholder="请选择类别"
|
||||
@change="handleFieldChange('cate', $event)"
|
||||
show-search
|
||||
:filter-option="filterCateOption"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="(name, value) in cateOptions"
|
||||
:key="value"
|
||||
:value="parseInt(value)"
|
||||
>
|
||||
{{ name }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
@@ -368,15 +397,29 @@ const animals = ref([])
|
||||
const farms = ref([])
|
||||
const pens = ref([])
|
||||
const batches = ref([])
|
||||
const cattleUsers = ref([]) // 品系数据(cattle_user表)
|
||||
const cattleTypes = ref([]) // 品种数据(cattle_type表)
|
||||
const loading = ref(false)
|
||||
const farmsLoading = ref(false)
|
||||
const pensLoading = ref(false)
|
||||
const batchesLoading = ref(false)
|
||||
const cattleUsersLoading = ref(false)
|
||||
const cattleTypesLoading = ref(false)
|
||||
const modalVisible = ref(false)
|
||||
const submitLoading = ref(false)
|
||||
const isEdit = ref(false)
|
||||
const formRef = ref()
|
||||
|
||||
// 类别选项(预定义映射)
|
||||
const cateOptions = ref({
|
||||
1: '犊牛',
|
||||
2: '育成母牛',
|
||||
3: '架子牛',
|
||||
4: '青年牛',
|
||||
5: '基础母牛',
|
||||
6: '育肥牛'
|
||||
})
|
||||
|
||||
// 分页相关
|
||||
const pagination = ref({
|
||||
current: 1,
|
||||
@@ -474,19 +517,38 @@ const columns = [
|
||||
title: '品系',
|
||||
dataIndex: 'strain', // 映射iot_cattle.strain,显示用途名称
|
||||
key: 'strain',
|
||||
width: 120
|
||||
width: 120,
|
||||
customRender: ({ text }) => {
|
||||
const user = cattleUsers.value.find(u => u.id === text)
|
||||
return user ? user.name : text || '-'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '品种',
|
||||
dataIndex: 'varieties', // 映射iot_cattle.varieties
|
||||
key: 'varieties',
|
||||
width: 120
|
||||
width: 120,
|
||||
customRender: ({ text }) => {
|
||||
const type = cattleTypes.value.find(t => t.id === text)
|
||||
return type ? type.name : text || '-'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '类别',
|
||||
dataIndex: 'cate', // 映射iot_cattle.cate
|
||||
key: 'cate',
|
||||
width: 100
|
||||
width: 100,
|
||||
customRender: ({ text }) => {
|
||||
const cateMap = {
|
||||
1: '犊牛',
|
||||
2: '育成母牛',
|
||||
3: '架子牛',
|
||||
4: '青年牛',
|
||||
5: '基础母牛',
|
||||
6: '育肥牛'
|
||||
}
|
||||
return cateMap[text] || text || '-'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '出生体重(kg)',
|
||||
@@ -611,6 +673,38 @@ const fetchBatches = async (farmId = null) => {
|
||||
}
|
||||
}
|
||||
|
||||
// 获取品系列表(cattle_user表)
|
||||
const fetchCattleUsers = async () => {
|
||||
try {
|
||||
cattleUsersLoading.value = true
|
||||
const response = await api.get('/cattle-user')
|
||||
if (response.success) {
|
||||
cattleUsers.value = response.data
|
||||
console.log('获取品系列表成功:', cattleUsers.value.length, '条记录')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取品系列表失败:', error)
|
||||
} finally {
|
||||
cattleUsersLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 获取品种和类别列表(cattle_type表)
|
||||
const fetchCattleTypes = async () => {
|
||||
try {
|
||||
cattleTypesLoading.value = true
|
||||
const response = await api.get('/cattle-type')
|
||||
if (response.success) {
|
||||
cattleTypes.value = response.data
|
||||
console.log('获取品种列表成功:', cattleTypes.value.length, '条记录')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取品种列表失败:', error)
|
||||
} finally {
|
||||
cattleTypesLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 字段变化监听函数
|
||||
const handleFieldChange = (fieldName, value) => {
|
||||
console.log(`字段 ${fieldName} 变化:`, value)
|
||||
@@ -625,6 +719,54 @@ const handleFieldChange = (fieldName, value) => {
|
||||
formData[fieldName] = value
|
||||
}
|
||||
|
||||
// 下拉框过滤选项方法
|
||||
const filterOption = (input, option) => {
|
||||
return option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}
|
||||
|
||||
// 类别下拉框过滤选项方法
|
||||
const filterCateOption = (input, option) => {
|
||||
return option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}
|
||||
|
||||
// 绑定耳标到iot_jbq_client表
|
||||
const bindEarTag = async (earNumber, cattleId) => {
|
||||
try {
|
||||
console.log('开始绑定耳标:', earNumber, '到牛只ID:', cattleId)
|
||||
|
||||
// 查找对应的耳标设备
|
||||
const response = await api.get('/iot-jbq-client', {
|
||||
params: { cid: earNumber }
|
||||
})
|
||||
|
||||
if (response.success && response.data && response.data.length > 0) {
|
||||
const device = response.data[0]
|
||||
console.log('找到耳标设备:', device)
|
||||
|
||||
// 更新设备的绑定状态
|
||||
const updateResponse = await api.put(`/iot-jbq-client/${device.id}`, {
|
||||
bandge_status: 1, // 1表示已绑定
|
||||
bound_cattle_id: cattleId,
|
||||
bound_ear_number: earNumber
|
||||
})
|
||||
|
||||
if (updateResponse.success) {
|
||||
console.log('耳标绑定成功')
|
||||
message.success('耳标绑定成功')
|
||||
} else {
|
||||
console.error('耳标绑定失败:', updateResponse.message)
|
||||
message.warning('牛只档案创建成功,但耳标绑定失败')
|
||||
}
|
||||
} else {
|
||||
console.warn('未找到对应的耳标设备:', earNumber)
|
||||
message.warning('牛只档案创建成功,但未找到对应的耳标设备')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('耳标绑定失败:', error)
|
||||
message.warning('牛只档案创建成功,但耳标绑定失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 处理动物类型选择变化
|
||||
const handleTypeChange = (value) => {
|
||||
console.log('=== 动物类型变化 ===')
|
||||
@@ -775,6 +917,8 @@ const openModal = () => {
|
||||
// 加载必需数据
|
||||
const loadRequiredData = () => {
|
||||
fetchFarms()
|
||||
fetchCattleUsers()
|
||||
fetchCattleTypes()
|
||||
}
|
||||
|
||||
// 提交表单(使用iot_cattle API)
|
||||
@@ -789,11 +933,20 @@ const handleSubmit = async () => {
|
||||
console.log('是否编辑模式:', isEdit.value);
|
||||
console.log('原始表单数据:', JSON.parse(JSON.stringify(formData)));
|
||||
|
||||
// 处理日期格式
|
||||
// 检查必填字段
|
||||
const requiredFields = ['earNumber', 'sex', 'strain', 'varieties', 'cate', 'birthWeight', 'birthday', 'orgId'];
|
||||
const missingFields = requiredFields.filter(field => !formData[field] && formData[field] !== 0);
|
||||
if (missingFields.length > 0) {
|
||||
console.error('缺少必填字段:', missingFields);
|
||||
message.error(`缺少必填字段: ${missingFields.join(', ')}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理日期格式 - 转换为时间戳(秒)
|
||||
const submitData = {
|
||||
...formData,
|
||||
birthday: formData.birthday ? formData.birthday.format('YYYY-MM-DD') : null,
|
||||
weightCalculateTime: formData.weightCalculateTime ? formData.weightCalculateTime.format('YYYY-MM-DD') : null
|
||||
birthday: formData.birthday ? Math.floor(formData.birthday.valueOf() / 1000) : null,
|
||||
weightCalculateTime: formData.weightCalculateTime ? Math.floor(formData.weightCalculateTime.valueOf() / 1000) : null
|
||||
}
|
||||
|
||||
// 如果是新增操作,移除id字段
|
||||
@@ -815,6 +968,11 @@ const handleSubmit = async () => {
|
||||
console.log('服务器响应:', response);
|
||||
|
||||
if (response.success) {
|
||||
// 如果创建成功且有耳标号,尝试绑定耳标
|
||||
if (!isEdit.value && formData.earNumber) {
|
||||
await bindEarTag(formData.earNumber, response.data.id)
|
||||
}
|
||||
|
||||
message.success(isEdit.value ? '更新牛只档案成功' : '创建牛只档案成功')
|
||||
modalVisible.value = false
|
||||
await fetchAnimals()
|
||||
@@ -1210,6 +1368,8 @@ onMounted(() => {
|
||||
fetchFarms()
|
||||
fetchPens()
|
||||
fetchBatches()
|
||||
fetchCattleUsers()
|
||||
fetchCattleTypes()
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -119,18 +119,32 @@
|
||||
</template>
|
||||
</a-input>
|
||||
<a-select
|
||||
v-model="searchType"
|
||||
placeholder="围栏类型"
|
||||
class="type-filter"
|
||||
@change="handleSearch"
|
||||
v-model="selectedFenceName"
|
||||
placeholder="选择围栏名称"
|
||||
class="fence-name-select"
|
||||
@change="handleFenceNameSelect"
|
||||
allowClear
|
||||
show-search
|
||||
:filter-option="filterFenceNameOption"
|
||||
:loading="fenceNamesLoading"
|
||||
>
|
||||
<a-select-option value="">全部类型</a-select-option>
|
||||
<a-select-option value="grazing">放牧区</a-select-option>
|
||||
<a-select-option value="safety">安全区</a-select-option>
|
||||
<a-select-option value="restricted">限制区</a-select-option>
|
||||
<a-select-option value="collector">收集区</a-select-option>
|
||||
<a-select-option
|
||||
v-for="fence in fenceList"
|
||||
:key="fence.id"
|
||||
:value="fence.name"
|
||||
>
|
||||
{{ fence.name }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
|
||||
<a-button
|
||||
type="default"
|
||||
@click="clearAllSearch"
|
||||
class="clear-search-btn"
|
||||
:disabled="!searchValue && !selectedFenceName && !searchType"
|
||||
>
|
||||
清除搜索
|
||||
</a-button>
|
||||
</div>
|
||||
<div class="fence-stats">
|
||||
<a-statistic
|
||||
@@ -264,6 +278,8 @@ const submitting = ref(false)
|
||||
const fenceModalVisible = ref(false)
|
||||
const searchValue = ref('')
|
||||
const searchType = ref('')
|
||||
const selectedFenceName = ref('')
|
||||
const fenceNamesLoading = ref(false)
|
||||
const selectedFence = ref(null)
|
||||
const editingFence = ref(null)
|
||||
|
||||
@@ -306,8 +322,13 @@ let allMarkers = []
|
||||
const filteredFenceList = computed(() => {
|
||||
let filtered = fenceList.value
|
||||
|
||||
// 按名称和描述搜索
|
||||
if (searchValue.value) {
|
||||
// 按围栏名称选择过滤
|
||||
if (selectedFenceName.value) {
|
||||
filtered = filtered.filter(fence => fence.name === selectedFenceName.value)
|
||||
}
|
||||
|
||||
// 按名称和描述搜索(当没有选择具体围栏名称时)
|
||||
if (searchValue.value && !selectedFenceName.value) {
|
||||
const searchLower = searchValue.value.toLowerCase()
|
||||
filtered = filtered.filter(fence =>
|
||||
fence.name.toLowerCase().includes(searchLower) ||
|
||||
@@ -700,7 +721,7 @@ const handleFenceSubmit = async () => {
|
||||
// 保存围栏
|
||||
const result = await api.electronicFence.createFence(fenceData)
|
||||
|
||||
if (result && result.id) {
|
||||
if (result && result.data && result.data.id) {
|
||||
// 保存坐标点
|
||||
const pointsData = drawingState.currentPoints.map((point, index) => ({
|
||||
lng: point.lng,
|
||||
@@ -709,10 +730,16 @@ const handleFenceSubmit = async () => {
|
||||
description: `围栏点${index + 1}`
|
||||
}))
|
||||
|
||||
await api.electronicFencePoints.createPoints({
|
||||
fence_id: result.id,
|
||||
points: pointsData
|
||||
})
|
||||
try {
|
||||
await api.electronicFencePoints.createPoints({
|
||||
fence_id: result.data.id,
|
||||
points: pointsData
|
||||
})
|
||||
console.log('坐标点创建成功')
|
||||
} catch (pointError) {
|
||||
console.error('坐标点创建失败:', pointError)
|
||||
message.warning('围栏创建成功,但坐标点保存失败: ' + pointError.message)
|
||||
}
|
||||
|
||||
console.log('围栏创建成功:', result)
|
||||
message.success('围栏创建成功')
|
||||
@@ -1090,6 +1117,27 @@ const handleSearch = () => {
|
||||
// 搜索逻辑在计算属性中处理
|
||||
}
|
||||
|
||||
// 处理围栏名称选择
|
||||
const handleFenceNameSelect = (value) => {
|
||||
selectedFenceName.value = value
|
||||
// 清空文本搜索,避免冲突
|
||||
if (value) {
|
||||
searchValue.value = ''
|
||||
}
|
||||
}
|
||||
|
||||
// 过滤围栏名称选项
|
||||
const filterFenceNameOption = (input, option) => {
|
||||
return option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}
|
||||
|
||||
// 清除所有搜索条件
|
||||
const clearAllSearch = () => {
|
||||
searchValue.value = ''
|
||||
selectedFenceName.value = ''
|
||||
searchType.value = ''
|
||||
}
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
console.log('电子围栏组件已挂载')
|
||||
@@ -1233,15 +1281,26 @@ onMounted(() => {
|
||||
|
||||
.search-section {
|
||||
margin-bottom: 15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
margin-bottom: 10px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.fence-name-select {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.type-filter {
|
||||
width: 100%;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.clear-search-btn {
|
||||
width: 100%;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.fence-stats {
|
||||
|
||||
Reference in New Issue
Block a user