修改政府端前端,银行端小程序和后端接口

This commit is contained in:
2025-09-26 17:52:50 +08:00
parent 852adbcfff
commit 00dfa83fd1
237 changed files with 9172 additions and 33500 deletions

View File

@@ -66,11 +66,11 @@
<script setup>
import { ref } from 'vue'
import { useRouter } from 'vue-router'
import {
MenuUnfoldOutlined,
MenuFoldOutlined,
UserOutlined,
DownOutlined,
import {
MenuUnfoldOutlined,
MenuFoldOutlined,
UserOutlined,
DownOutlined,
LogoutOutlined
} from '@ant-design/icons-vue'
import { useUserStore } from '@/stores/user'

View File

@@ -57,240 +57,105 @@
</a-menu-item>
</a-sub-menu>
<!-- 无纸化服务 -->
<a-sub-menu key="paperless">
<template #icon><FileTextOutlined /></template>
<template #title>
<span>无纸化服务</span>
</template>
<!-- 无纸化防疫 -->
<a-sub-menu key="paperless-epidemic">
<template #title>
<span>无纸化防疫</span>
</template>
<a-menu-item key="/paperless/epidemic"><span>疫情防控</span></a-menu-item>
<a-menu-item key="/paperless/epidemic/epidemic-agency"><span>防疫机构管理</span></a-menu-item>
<a-menu-item key="/paperless/epidemic/epidemic-record"><span>防疫记录</span></a-menu-item>
<a-menu-item key="/paperless/epidemic/vaccine-management"><span>疫苗管理</span></a-menu-item>
<a-menu-item key="/paperless/epidemic/epidemic-activity"><span>防疫活动管理</span></a-menu-item>
</a-sub-menu>
<!-- 无纸化检疫 -->
<a-sub-menu key="paperless-quarantine">
<template #title>
<span>无纸化检疫</span>
</template>
<a-menu-item key="/paperless/quarantine/declaration"><span>检疫审批</span></a-menu-item>
<a-menu-item key="/paperless/quarantine/record-search"><span>检疫证查询</span></a-menu-item>
<a-menu-item key="/paperless/quarantine/report-export"><span>检疫证清单</span></a-menu-item>
</a-sub-menu>
</a-sub-menu>
<!-- 屠宰无害化 -->
<a-sub-menu key="slaughter">
<template #icon><SafetyOutlined /></template>
<template #title>
<span>屠宰无害化</span>
</template>
<!-- 屠宰管理 -->
<a-sub-menu key="slaughter-management">
<template #title>
<span>屠宰管理</span>
</template>
<a-menu-item key="/slaughter/slaughterhouse"><span>屠宰场</span></a-menu-item>
</a-sub-menu>
<!-- 无害化处理 -->
<a-sub-menu key="harmless-treatment">
<template #title>
<span>害化处理</span>
</template>
<a-menu-item key="/slaughter/harmless/place"><span>无害化场所</span></a-menu-item>
<a-menu-item key="/slaughter/harmless/registration"><span>无害化登记</span></a-menu-item>
</a-sub-menu>
<a-menu-item key="/slaughter/slaughterhouse">
<span>屠宰场</span>
</a-menu-item>
<a-menu-item key="/slaughter/harmless/place">
<span>无害化场所</span>
</a-menu-item>
</a-sub-menu>
<!-- 无纸化防疫 -->
<a-sub-menu key="paperless-epidemic">
<template #icon><FileTextOutlined /></template>
<template #title>
<span>纸化防疫</span>
</template>
<a-menu-item key="paperless/epidemic"><span>防疫首页</span></a-menu-item>
<a-menu-item key="paperless/epidemic/epidemic-agency"><span>防疫机构管理</span></a-menu-item>
<a-menu-item key="paperless/epidemic/epidemic-record"><span>防疫记录</span></a-menu-item>
<a-menu-item key="paperless/epidemic/vaccine-management"><span>疫苗管理</span></a-menu-item>
<a-menu-item key="paperless/epidemic/epidemic-activity"><span>防疫活动管理</span></a-menu-item>
</a-sub-menu>
<!-- 无纸化检疫 -->
<a-sub-menu key="paperless-quarantine">
<template #icon><SafetyOutlined /></template>
<template #title>
<span>无纸化检疫</span>
</template>
<a-menu-item key="paperless/quarantine/declaration"><span>检疫审批</span></a-menu-item>
<a-menu-item key="paperless/quarantine/record-query"><span>检疫证查询</span></a-menu-item>
<a-menu-item key="paperless/quarantine/config"><span>检疫站清单</span></a-menu-item>
</a-sub-menu>
<!-- 生资认证 -->
<a-menu-item key="/examine/index">
<a-menu-item key="examine/index">
<template #icon><CheckCircleOutlined /></template>
<span>生资认证</span>
</a-menu-item>
<!-- 线上问诊 -->
<a-menu-item key="/consultation">
<template #icon><MedicineBoxOutlined /></template>
<span>线上问诊</span>
</a-menu-item>
<!-- 养牛学院 -->
<a-menu-item key="/academy">
<template #icon><ReadOutlined /></template>
<a-menu-item key="academy">
<template #icon><BookOutlined /></template>
<span>养牛学院</span>
</a-menu-item>
<!-- 消息通知 -->
<a-menu-item key="/notification">
<template #icon><BellOutlined /></template>
<span>消息通知</span>
<!-- 设备预警 -->
<a-menu-item key="device-alert">
<template #icon><ExclamationCircleOutlined /></template>
<span>设备预警</span>
</a-menu-item>
</a-menu>
</template>
<script lang="js">
import { ref, onMounted, watch, h } from 'vue'
<script setup>
import { ref, onMounted, watch } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { useAuthStore } from '@/stores/auth'
import {
DashboardOutlined,
BarChartOutlined,
LineChartOutlined,
UserOutlined,
TeamOutlined,
HddOutlined,
ExperimentOutlined,
FileTextOutlined,
SafetyOutlined,
BankOutlined,
CheckCircleOutlined,
ShoppingOutlined,
CommentOutlined,
MedicineBoxOutlined,
ReadOutlined,
BellOutlined
} from '@ant-design/icons-vue'
import { BarChartOutlined, LineChartOutlined, UserOutlined, TeamOutlined, HddOutlined, SafetyOutlined, FileTextOutlined, CheckCircleOutlined, BookOutlined, ExclamationCircleOutlined } from '@ant-design/icons-vue'
export default {
setup() {
const router = useRouter()
const route = useRoute()
const authStore = useAuthStore()
const router = useRouter()
const route = useRoute()
// 当前选中的菜单项
const selectedKeys = ref([])
// 当前选中的菜单项
const selectedKeys = ref([route.path])
// 展开的菜单项
const openKeys = ref([])
// 展开的菜单项
const openKeys = ref([])
// 检查是否有权限
const hasPermission = (permission) => {
return authStore.hasPermission([permission])
}
// 处理菜单选择
const handleMenuSelect = ({ key }) => {
// 确保路由存在
if (key) {
console.log('导航到路由:', key)
// 使用replace而不是push避免历史记录堆积
router.replace(key).catch(err => {
console.error('路由导航失败:', err)
})
}
}
// 处理菜单选择
const handleMenuSelect = ({ key }) => {
if (key) {
// 确保使用绝对路径进行路由跳转
const absolutePath = key.startsWith('/') ? key : `/${key}`
router.replace(absolutePath)
}
}
// 处理展开/收起
const handleOpenChange = (keys) => {
// 保留所有打开的菜单项,不折叠
openKeys.value = keys
}
// 替换获取父级菜单key -> 获取需要展开的菜单keys
const getOpenMenuKeys = (path) => {
// 顶级目录映射
const topLevelMap = {
'/index': '',
'/price': '',
'/personnel': 'personnel',
'/farmer': '',
'/smart-warehouse': 'smart-warehouse',
'/paperless': 'paperless',
'/slaughter': '',
'/examine': '',
'/consultation': '',
'/academy': '',
'/notification': ''
}
// 二级目录映射 - 确保点击三级目录时保持二级目录展开
if (path.startsWith('/paperless/epidemic')) {
return ['paperless', 'paperless-epidemic']
}
if (path.startsWith('/paperless/quarantine')) {
return ['paperless', 'paperless-quarantine']
}
// 屠宰管理相关路径处理
if (path.startsWith('/slaughter/slaughterhouse')) {
return ['slaughter', 'slaughter-management']
}
// 无害化处理相关路径处理
if (path.startsWith('/slaughter/harmless')) {
return ['slaughter', 'harmless-treatment']
}
for (const [prefix, key] of Object.entries(topLevelMap)) {
if (key && path.startsWith(prefix)) {
return [key]
}
}
// 特殊处理智慧仓库路径
if (path.includes('smart-warehouse')) {
return ['smart-warehouse']
}
return []
}
// 更新选中状态
const updateSelectedState = () => {
const currentPath = route.path
selectedKeys.value = [currentPath]
const keys = getOpenMenuKeys(currentPath)
// 合并现有打开的菜单和新需要打开的菜单,确保已打开的菜单不会关闭
openKeys.value = [...new Set([...openKeys.value, ...keys])]
}
// 监听路由变化
watch(
() => route.path,
() => {
updateSelectedState()
(newPath) => {
selectedKeys.value = [newPath]
}
)
// 返回需要在模板中使用的内容
return {
hasPermission,
handleMenuSelect,
handleOpenChange,
selectedKeys,
openKeys,
DashboardOutlined: () => h(DashboardOutlined),
BarChartOutlined: () => h(BarChartOutlined),
LineChartOutlined: () => h(LineChartOutlined),
UserOutlined: () => h(UserOutlined),
TeamOutlined: () => h(TeamOutlined),
HddOutlined: () => h(HddOutlined),
ExperimentOutlined: () => h(ExperimentOutlined),
FileTextOutlined: () => h(FileTextOutlined),
SafetyOutlined: () => h(SafetyOutlined),
BankOutlined: () => h(BankOutlined),
CheckCircleOutlined: () => h(CheckCircleOutlined),
ShoppingOutlined: () => h(ShoppingOutlined),
CommentOutlined: () => h(CommentOutlined),
MedicineBoxOutlined: () => h(MedicineBoxOutlined),
ReadOutlined: () => h(ReadOutlined),
BellOutlined: () => h(BellOutlined)
}
// 组件挂载时初始化
onMounted(() => {
updateSelectedState()
// 确保选中当前路由
selectedKeys.value = [route.path]
})
}
}
</script>
<style scoped>
@@ -298,37 +163,4 @@ onMounted(() => {
height: 100%;
border-right: 0;
}
/* 自定义菜单项样式 */
:deep(.ant-menu-item) {
transition: all 0.3s;
}
:deep(.ant-menu-item:hover) {
background-color: rgba(255, 255, 255, 0.1) !important;
}
:deep(.ant-menu-item-selected) {
background-color: #1890ff !important;
}
/* 子菜单样式 */
:deep(.ant-menu-submenu-title) {
transition: all 0.3s;
}
:deep(.ant-menu-submenu-title:hover) {
background-color: rgba(255, 255, 255, 0.1) !important;
}
/* 图标样式 */
:deep(.anticon) {
font-size: 16px;
}
/* 菜单项文本样式 */
:deep(.ant-menu-item .ant-menu-title-content),
:deep(.ant-menu-submenu-title .ant-menu-title-content) {
font-size: 14px;
}
</style>

View File

@@ -145,35 +145,36 @@ const routes = [
component: () => import('@/views/paperless/epidemic/vaccine-management/VaccineManagement.vue'),
meta: { title: '疫苗管理' }
},
{
path: 'paperless/epidemic/epidemic-activity',
name: 'EpidemicActivityManagement',
component: () => import('@/views/paperless/epidemic/epidemic-activity/EpidemicActivityManagement.vue'),
meta: { title: '防疫活动管理' }
{
path: 'paperless/epidemic/epidemic-activity',
name: 'EpidemicActivityManagement',
component: () => import('@/views/paperless/epidemic/epidemic-activity/EpidemicActivityManagement.vue'),
meta: { title: '防疫活动管理' }
},
{ // 无纸化检疫主页
// 无纸化检疫相关路由
{
path: 'paperless/quarantine',
name: 'QuarantineHome',
component: () => import('@/views/paperless/QuarantineHome.vue'),
meta: { title: '无纸化检疫' }
},
{ // 建议审批
{
path: 'paperless/quarantine/declaration',
name: 'QuarantineDeclaration',
component: () => import('@/views/paperless/quarantine/QuarantineDeclaration.vue'),
meta: { title: '建议审批' }
meta: { title: '检疫审批' }
},
{ // 检疫证查询
path: 'paperless/quarantine/record-search',
name: 'QuarantineRecordSearch',
component: () => import('@/views/paperless/quarantine/QuarantineRecordSearch.vue'),
{
path: 'paperless/quarantine/record-query',
name: 'QuarantineRecordQuery',
component: () => import('@/views/paperless/quarantine/QuarantineRecordQuery.vue'),
meta: { title: '检疫证查询' }
},
{ // 检疫证清单
path: 'paperless/quarantine/report-export',
name: 'QuarantineReportExport',
component: () => import('@/views/paperless/quarantine/QuarantineReportExport.vue'),
meta: { title: '检疫清单' }
{
path: 'paperless/quarantine/config',
name: 'QuarantineConfig',
component: () => import('@/views/paperless/quarantine/QuarantineConfig.vue'),
meta: { title: '检疫清单' }
},
{
path: 'slaughter',
@@ -235,12 +236,18 @@ const routes = [
component: CattleAcademy,
meta: { title: '养牛学院' }
},
{
{
path: 'notification',
name: 'MessageNotification',
component: MessageNotification,
meta: { title: '消息通知' }
},
{
path: 'device-warning',
name: 'DeviceWarning',
component: () => import('@/views/DeviceWarning.vue'),
meta: { title: '设备预警' }
},
// {
// path: 'users',
// name: 'UserManagement',
@@ -289,12 +296,43 @@ const routes = [
// component: EpidemicManagement,
// meta: { title: '疫情管理' }
// },
// {
// {
// path: 'visualization',
// name: 'VisualAnalysis',
// component: VisualAnalysis,
// meta: { title: '可视化分析' }
// }
// 无纸化防疫相关路由
{
path: 'paperless/epidemic',
name: 'EpidemicHome',
component: () => import('@/views/paperless/EpidemicHome.vue'),
meta: { title: '防疫首页' }
},
{
path: 'paperless/epidemic/epidemic-agency',
name: 'EpidemicAgencyManagement',
component: () => import('@/views/paperless/epidemic/epidemic-agency/EpidemicAgency.vue'),
meta: { title: '防疫机构管理' }
},
{
path: 'paperless/epidemic/epidemic-record',
name: 'EpidemicRecordManagement',
component: () => import('@/views/paperless/epidemic/epidemic-record/EpidemicRecordManagement.vue'),
meta: { title: '防疫记录' }
},
{
path: 'paperless/epidemic/vaccine-management',
name: 'VaccineManagement',
component: () => import('@/views/paperless/epidemic/vaccine-management/VaccineManagement.vue'),
meta: { title: '疫苗管理' }
},
{
path: 'paperless/epidemic/epidemic-activity',
name: 'EpidemicActivityManagement',
component: () => import('@/views/paperless/epidemic/epidemic-activity/EpidemicActivityManagement.vue'),
meta: { title: '防疫活动管理' }
}
]
}
]

View File

@@ -1,4 +1,5 @@
import { ref } from 'vue'
import { computed, ref } from 'vue'
import { defineStore } from 'pinia'
import { message } from 'ant-design-vue'
import router from '@/router'
@@ -93,6 +94,9 @@ export const useAuthStore = defineStore('auth', () => {
const isLoggedIn = () => {
return !!token.value
}
// 计算属性,用于在模板中直接使用
const isAuthenticated = computed(() => isLoggedIn())
return {
token,
@@ -104,6 +108,7 @@ export const useAuthStore = defineStore('auth', () => {
login,
logout,
hasPermission,
isLoggedIn
isLoggedIn,
isAuthenticated
}
})

View File

@@ -10,10 +10,10 @@ const instance = axios.create({
}
})
// 请求拦截器 - 从localStorage中获取token
// 请求拦截器
instance.interceptors.request.use(
config => {
// 从localStorage获取token
// 从localStorage获取token
const token = localStorage.getItem('token')
// 如果有token添加到请求头
if (token) {
@@ -38,7 +38,7 @@ instance.interceptors.response.use(
// 根据不同的状态码处理错误
switch (error.response.status) {
case 401:
// 未授权,跳转到登录页面
// 未授权,清除token并跳转到登录页面
localStorage.removeItem('token')
window.location.href = '/login'
message.error('登录已过期,请重新登录')
@@ -53,243 +53,275 @@ instance.interceptors.response.use(
message.error('服务器内部错误')
break
default:
message.error('请求失败')
message.error(error.response.data.message || '请求失败')
}
} else if (error.request) {
// 请求发出但没有收到响应
message.error('网络连接失败')
message.error('网络错误,请检查网络连接')
} else {
// 请求配置出错
message.error('请求配置错')
message.error('请求配置错')
}
return Promise.reject(error)
}
)
// 认证相关的API
const auth = {
// 登录
login: data => instance.post('/auth/login', data),
// 获取用户信息
getUserInfo: () => instance.get('/auth/userInfo'),
// 退出登录
logout: () => instance.post('/auth/logout'),
// 重置密码
resetPassword: data => instance.post('/auth/resetPassword', data)
}
// 用户管理相关的API
const user = {
// 获取用户列表
getList: params => instance.get('/user/list', { params }),
// 新增用户
create: data => instance.post('/user/create', data),
// 编辑用户
update: data => instance.post('/user/update', data),
// 删除用户
delete: id => instance.post(`/user/delete/${id}`),
// 禁用/启用用户
toggleStatus: data => instance.post('/user/toggleStatus', data),
// 重置密码
resetPassword: id => instance.post(`/user/resetPassword/${id}`)
}
// 监管相关的API
const supervision = {
// 获取监管列表
getList: params => instance.get('/supervision/list', { params }),
// 获取监管详情
getDetail: id => instance.get(`/supervision/detail/${id}`),
// 新增监管
create: data => instance.post('/supervision/create', data),
// 更新监管
update: data => instance.post('/supervision/update', data),
// 删除监管
delete: id => instance.post(`/supervision/delete/${id}`)
}
// 审批相关的API
const approval = {
// 获取审批列表
getList: params => instance.get('/approval/list', { params }),
// 获取审批详情
getDetail: id => instance.get(`/approval/detail/${id}`),
// 提交审批
submit: data => instance.post('/approval/submit', data),
// 审批操作
approve: data => instance.post('/approval/approve', data),
// 驳回操作
reject: data => instance.post('/approval/reject', data),
// 撤回操作
withdraw: id => instance.post(`/approval/withdraw/${id}`)
}
// 疫情监控相关的API
const epidemic = {
// 获取疫情监控列表
getList: params => instance.get('/epidemic/list', { params }),
// 获取疫情监控详情
getDetail: id => instance.get(`/epidemic/detail/${id}`),
// 新增疫情监控
create: data => instance.post('/epidemic/create', data),
// 更新疫情监控
update: data => instance.post('/epidemic/update', data),
// 删除疫情监控
delete: id => instance.post(`/epidemic/delete/${id}`),
// 处理疫情
handle: data => instance.post('/epidemic/handle', data)
}
// 数据可视化相关的API
const visualization = {
// 获取养殖情况统计
getBreedingStats: params => instance.get('/visualization/breedingStats', { params }),
// 获取疫情趋势
getEpidemicTrend: params => instance.get('/visualization/epidemicTrend', { params }),
// 获取监管效果
getSupervisionEffect: params => instance.get('/visualization/supervisionEffect', { params })
}
// 文件管理相关的API
const file = {
// 上传文件
upload: data => instance.post('/file/upload', data),
// 下载文件
download: id => instance.get(`/file/download/${id}`),
// 获取文件列表
getList: params => instance.get('/file/list', { params }),
// 删除文件
delete: id => instance.post(`/file/delete/${id}`)
}
// 人员管理相关的API
const staff = {
// 获取人员列表
getList: params => instance.get('/staff/list', { params }),
// 新增人员
create: data => instance.post('/staff/create', data),
// 更新人员
update: data => instance.post('/staff/update', data),
// 删除人员
delete: id => instance.post(`/staff/delete/${id}`),
// 获取人员详情
getDetail: id => instance.get(`/staff/detail/${id}`)
}
// 服务管理相关的API
const service = {
// 获取服务列表
getList: params => instance.get('/service/list', { params }),
// 新增服务
create: data => instance.post('/service/create', data),
// 更新服务
update: data => instance.post('/service/update', data),
// 删除服务
delete: id => instance.post(`/service/delete/${id}`),
// 获取服务详情
getDetail: id => instance.get(`/service/detail/${id}`)
}
// 仓库管理相关的API
const warehouse = {
// 获取仓库列表
getList: params => instance.get('/warehouse/list', { params }),
// 新增仓库
create: data => instance.post('/warehouse/create', data),
// 更新仓库
update: data => instance.post('/warehouse/update', data),
// 删除仓库
delete: id => instance.post(`/warehouse/delete/${id}`),
// 获取仓库详情
getDetail: id => instance.get(`/warehouse/detail/${id}`)
}
// 系统设置相关的API
const system = {
// 获取系统设置
getSettings: () => instance.get('/system/settings'),
// 更新系统设置
updateSettings: data => instance.post('/system/updateSettings', data),
// 获取操作日志
getOperationLogs: params => instance.get('/system/operationLogs', { params })
}
// 政府管理相关的API
const government = {
// 行政人员管理
adminStaff: {
// 获取行政人员列表
getList: params => instance.get('/government/admin-staff/list', { params }),
// 新增行政人员
create: data => instance.post('/government/admin-staff/create', data),
// 编辑行政人员
update: data => instance.post('/government/admin-staff/update', data),
// 删除行政人员
delete: id => instance.post(`/government/admin-staff/delete/${id}`),
// API接口定义
const api = {
// 认证相关API
auth: {
// 登录
login: (data) => instance.post('/auth/login', data),
// 获取用户信息
getUserInfo: () => instance.get('/auth/userinfo'),
// 退出登录
logout: () => instance.post('/auth/logout'),
// 重置密码
resetPassword: id => instance.post(`/government/admin-staff/reset-password/${id}`)
resetPassword: (data) => instance.post('/auth/reset-password', data)
},
// 部门管理
departments: {
// 获取部门列表
getList: () => instance.get('/government/departments/list'),
// 新增部门
create: data => instance.post('/government/departments/create', data),
// 编辑部门
update: data => instance.post('/government/departments/update', data),
// 删除部门
delete: id => instance.post(`/government/departments/delete/${id}`)
// 用户管理相关API
user: {
// 获取用户列表
getList: (params) => instance.get('/users', { params }),
// 获取单个用户信息
getDetail: (id) => instance.get(`/users/${id}`),
// 创建用户
create: (data) => instance.post('/users', data),
// 更新用户
update: (id, data) => instance.put(`/users/${id}`, data),
// 删除用户
delete: (id) => instance.delete(`/users/${id}`),
// 批量删除用户
batchDelete: (ids) => instance.post('/users/batch-delete', { ids }),
// 更新用户状态
updateStatus: (id, status) => instance.put(`/users/${id}/status`, { status })
},
// 岗位管理
positions: {
// 获取岗位列表
getList: () => instance.get('/government/positions/list'),
// 新增岗位
create: data => instance.post('/government/positions/create', data),
// 编辑岗位
update: data => instance.post('/government/positions/update', data),
// 删除岗位
delete: id => instance.post(`/government/positions/delete/${id}`)
// 监管相关API
supervision: {
// 获取监管统计数据
getStats: () => instance.get('/supervision/stats'),
// 获取监管任务列表
getTasks: (params) => instance.get('/supervision/tasks', { params }),
// 获取监管任务详情
getTaskDetail: (id) => instance.get(`/supervision/tasks/${id}`),
// 创建监管任务
createTask: (data) => instance.post('/supervision/tasks', data),
// 更新监管任务
updateTask: (id, data) => instance.put(`/supervision/tasks/${id}`, data),
// 删除监管任务
deleteTask: (id) => instance.delete(`/supervision/tasks/${id}`)
},
// 养殖户管理
// 审批相关API
approval: {
// 获取审批流程列表
getList: (params) => instance.get('/approval', { params }),
// 创建审批流程
create: (data) => instance.post('/approval', data),
// 获取审批详情
getDetail: (id) => instance.get(`/approval/${id}`),
// 更新审批状态
updateStatus: (id, status) => instance.put(`/approval/${id}/status`, { status })
},
// 疫情监控相关API
epidemic: {
// 获取疫情统计数据
getStats: () => instance.get('/epidemic/stats'),
// 获取疫苗接种数据
getVaccinationData: (params) => instance.get('/epidemic/vaccination', { params }),
// 获取检测数据
getTestData: (params) => instance.get('/epidemic/test', { params }),
// 防疫机构管理
agencies: {
// 获取防疫机构列表
getList: (params) => instance.get('/epidemic/agencies', { params }),
// 获取防疫机构详情
getDetail: (id) => instance.get(`/epidemic/agencies/${id}`),
// 创建防疫机构
create: (data) => instance.post('/epidemic/agencies', data),
// 更新防疫机构
update: (id, data) => instance.put(`/epidemic/agencies/${id}`, data),
// 删除防疫机构
delete: (id) => instance.delete(`/epidemic/agencies/${id}`),
// 切换防疫机构状态
toggleStatus: (id) => instance.patch(`/epidemic/agencies/${id}/status`)
}
},
// 数据可视化相关API
visualization: {
// 获取可视化数据
getData: (params) => instance.get('/visualization/data', { params })
},
// 文件管理相关API
file: {
// 获取文件列表
getList: (params) => instance.get('/files', { params }),
// 上传文件
upload: (file, onUploadProgress) => {
const formData = new FormData()
formData.append('file', file)
return instance.post('/files/upload', formData, {
headers: {
'Content-Type': 'multipart/form-data'
},
onUploadProgress
})
},
// 下载文件
download: (id) => instance.get(`/files/${id}/download`, { responseType: 'blob' }),
// 删除文件
delete: (id) => instance.delete(`/files/${id}`)
},
// 人员管理相关API
personnel: {
// 获取人员列表
getList: (params) => instance.get('/personnel', { params }),
// 创建人员
create: (data) => instance.post('/personnel', data),
// 更新人员
update: (id, data) => instance.put(`/personnel/${id}`, data),
// 删除人员
delete: (id) => instance.delete(`/personnel/${id}`)
},
// 服务管理相关API
service: {
// 获取服务列表
getList: (params) => instance.get('/service', { params }),
// 创建服务
create: (data) => instance.post('/service', data),
// 更新服务
update: (id, data) => instance.put(`/service/${id}`, data),
// 删除服务
delete: (id) => instance.delete(`/service/${id}`)
},
// 仓库管理相关API
warehouse: {
// 获取物资列表
getList: (params) => instance.get('/warehouse', { params }),
// 获取单个物资详情
getDetail: (id) => instance.get(`/warehouse/${id}`),
// 创建物资
create: (data) => instance.post('/warehouse', data),
// 更新物资
update: (id, data) => instance.put(`/warehouse/${id}`, data),
// 删除物资
delete: (id) => instance.delete(`/warehouse/${id}`),
// 物资入库
stockIn: (data) => instance.post('/warehouse/in', data),
// 物资出库
stockOut: (data) => instance.post('/warehouse/out', data),
// 获取库存统计信息
getStats: () => instance.get('/warehouse/stats')
},
// 系统设置相关API
system: {
// 获取系统设置
getSettings: () => instance.get('/system/settings'),
// 更新系统设置
updateSettings: (data) => instance.put('/system/settings', data),
// 获取日志列表
getLogs: (params) => instance.get('/system/logs', { params })
},
// 政府管理相关API
government: {
// 行政人员管理
adminStaff: {
// 获取行政人员列表
getList: (params) => instance.get('/personnel', { params }),
// 创建行政人员
create: (data) => instance.post('/personnel', data),
// 更新行政人员
update: (id, data) => instance.put(`/personnel/${id}`, data),
// 删除行政人员
delete: (id) => instance.delete(`/personnel/${id}`),
// 重置行政人员密码
resetPassword: (id) => instance.post(`/personnel/${id}/reset-password`)
},
// 部门管理
departments: {
// 获取部门列表
getList: (params) => instance.get('/government/departments', { params })
},
// 岗位管理
positions: {
// 获取岗位列表
getList: (params) => instance.get('/government/positions', { params })
},
// 养殖户管理
farmers: {
// 获取养殖户列表
getList: params => instance.get('/government/farmers', { params }),
getList: (params) => instance.get('/government/farmers', { params }),
// 新增养殖户
create: data => instance.post('/government/farmers', data),
create: (data) => instance.post('/government/farmers', data),
// 编辑养殖户
update: (id, data) => instance.put(`/government/farmers/${id}`, data),
// 删除养殖户
delete: id => instance.delete(`/government/farmers/${id}`),
// 重置密码
resetPassword: id => instance.post(`/government/farmers/${id}/reset-password`)
delete: (id) => instance.delete(`/government/farmers/${id}`),
// 重置养殖户密码
resetPassword: (id) => instance.post(`/government/farmers/${id}/reset-password`)
},
// 养殖类型相关
farmTypes: {
// 获取养殖类型列表
getList: () => instance.get('/government/farm-types')
},
// 养殖种类相关
animalTypes: {
// 获取养殖种类列表
getList: () => instance.get('/government/animal-types')
// 养殖类型相关
farmTypes: {
// 获取养殖类型列表
getList: () => instance.get('/government/farm-types')
},
// 养殖种类相关
animalTypes: {
// 获取养殖种类列表
getList: () => instance.get('/government/animal-types')
},
// 智能项圈管理
collars: {
// 获取智能项圈列表
getList: (params) => instance.get('/government/collars', { params }),
// 新增智能项圈
create: (data) => instance.post('/government/collars', data),
// 编辑智能项圈
update: (id, data) => instance.put(`/government/collars/${id}`, data),
// 删除智能项圈
delete: (id) => instance.delete(`/government/collars/${id}`)
},
// 智能耳标管理
earmarks: {
// 获取智能耳标列表
getList: (params) => instance.get('/smart-earmark', { params }),
// 新增智能耳标
create: (data) => instance.post('/smart-earmark', data),
// 编辑智能耳标
update: (id, data) => instance.put(`/smart-earmark/${id}`, data),
// 删除智能耳标
delete: (id) => instance.delete(`/smart-earmark/${id}`)
},
// 智能主机管理
smartHosts: {
// 获取智能主机列表
getList: (params) => instance.get('/smart-host', { params }),
// 新增智能主机
create: (data) => instance.post('/smart-host', data),
// 编辑智能主机
update: (id, data) => instance.put(`/smart-host/${id}`, data),
// 删除智能主机
delete: (id) => instance.delete(`/smart-host/${id}`)
}
}
}
// 导出所有API
const api = {
auth,
user,
supervision,
approval,
epidemic,
visualization,
file,
staff,
service,
warehouse,
system,
government
}
export default api

View File

@@ -0,0 +1,284 @@
<template>
<div class="page-container">
<div class="header">
<div class="title">设备预警</div>
</div>
<div class="stats-container">
<div class="stat-card">
<div class="stat-value">8056</div>
<div class="stat-label">耳标预警</div>
</div>
<div class="stat-card">
<div class="stat-value">1</div>
<div class="stat-label">项圈预警</div>
</div>
<div class="stat-card">
<div class="stat-value">61</div>
<div class="stat-label">主机预警</div>
</div>
</div>
<a-card :bordered="false">
<div class="table-header">
<a-tabs v-model:activeKey="activeTab">
<a-tab-pane key="earTag" tab="智能耳标"></a-tab-pane>
<a-tab-pane key="neckband" tab="智能项圈"></a-tab-pane>
<a-tab-pane key="host" tab="智能主机"></a-tab-pane>
</a-tabs>
<a-input v-model:value="searchKeyword" placeholder="请输入养殖户" style="width: 200px;">
<template #suffix>
<span class="iconfont icon-sousuo"></span>
</template>
</a-input>
</div>
<a-table
:columns="columns"
:data-source="tableData"
:pagination="pagination"
row-key="id"
>
</a-table>
</a-card>
</div>
</template>
<script setup>
import { ref, reactive } from 'vue';
const activeTab = ref('earTag');
const searchKeyword = ref('');
const columns = [
{ title: '养殖场', dataIndex: 'farm', key: 'farm' },
{ title: '养殖户', dataIndex: 'farmer', key: 'farmer' },
{ title: '联系电话', dataIndex: 'phone', key: 'phone' },
{ title: '设备类型', dataIndex: 'deviceType', key: 'deviceType' },
{ title: '设备编号', dataIndex: 'deviceNumber', key: 'deviceNumber' },
{ title: '预警类型', dataIndex: 'alertType', key: 'alertType' },
];
const tableData = ref([
{ id: '1', farm: '扎旗大数据中心', farmer: '扎旗大数据中心', phone: '132****9345', deviceType: '智能耳标', deviceNumber: '2407100002', alertType: '设备离线' },
{ id: '2', farm: '扎旗大数据中心', farmer: '扎旗大数据中心', phone: '132****9345', deviceType: '智能耳标', deviceNumber: '2407100001', alertType: '设备离线' },
{ id: '3', farm: '扎旗大数据中心', farmer: '扎旗大数据中心', phone: '132****9345', deviceType: '智能耳标', deviceNumber: '2407402678', alertType: '设备离线' },
{ id: '4', farm: '扎旗大数据中心', farmer: '扎旗大数据中心', phone: '132****9345', deviceType: '智能耳标', deviceNumber: '2407402675', alertType: '设备离线' },
{ id: '5', farm: '扎旗大数据中心', farmer: '扎旗大数据中心', phone: '132****9345', deviceType: '智能耳标', deviceNumber: '2407402674', alertType: '设备离线' },
{ id: '6', farm: '139****8321_养殖场', farmer: '杜云鹏', phone: '139****8321', deviceType: '智能耳标', deviceNumber: '2404412397', alertType: '设备离线' },
{ id: '7', farm: '139****8321_养殖场', farmer: '杜云鹏', phone: '139****8321', deviceType: '智能耳标', deviceNumber: '2404412404', alertType: '设备离线' },
]);
const pagination = reactive({
current: 1,
pageSize: 10,
total: tableData.value.length,
});
</script>
<style scoped>
.page-container {
background-color: #f0f2f5;
padding: 24px;
}
.header {
margin-bottom: 24px;
}
.title {
font-size: 24px;
font-weight: bold;
}
.stats-container {
display: flex;
gap: 24px;
margin-bottom: 24px;
}
.stat-card {
background-color: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.09);
text-align: center;
flex: 1;
}
.stat-value {
font-size: 28px;
font-weight: bold;
color: #1890ff;
}
.stat-label {
font-size: 14px;
color: #595959;
}
.table-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
}
.important-messages {
display: flex;
flex-direction: column;
gap: 12px;
}
.important-message-item {
display: flex;
align-items: center;
gap: 12px;
padding: 16px;
border: 1px solid #ffccc7;
border-radius: 6px;
background-color: #fff2f0;
cursor: pointer;
transition: all 0.3s;
}
.important-message-item:hover {
background-color: #ff4d4f10;
}
.important-icon {
font-size: 24px;
color: #ff4d4f;
}
.important-content {
flex: 1;
}
.important-title {
font-weight: bold;
color: #ff4d4f;
margin-bottom: 4px;
}
.important-time {
font-size: 12px;
color: #999;
}
.view-btn {
margin-left: auto;
}
.no-data {
text-align: center;
color: #999;
padding: 24px;
}
.unread-dot {
width: 8px;
height: 8px;
background-color: #ff4d4f;
border-radius: 50%;
margin-right: 8px;
}
.message-title {
cursor: pointer;
transition: color 0.3s;
}
.message-title:hover {
color: #1890ff;
}
.unread-title {
font-weight: bold;
}
.important-title {
color: #ff4d4f;
font-weight: bold;
}
.top-icon {
font-size: 14px;
color: #faad14;
margin-left: 8px;
}
.message-content {
cursor: pointer;
color: #666;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.message-content:hover {
color: #1890ff;
}
.message-time {
color: #999;
font-size: 12px;
}
.detail-header {
margin-bottom: 24px;
padding-bottom: 16px;
border-bottom: 1px solid #f0f0f0;
}
.detail-title {
margin: 0 0 12px 0;
color: #333;
}
.detail-meta {
display: flex;
flex-wrap: wrap;
gap: 16px;
font-size: 14px;
color: #666;
}
.detail-content {
line-height: 1.8;
color: #333;
margin-bottom: 24px;
}
.attachments h4 {
margin: 0 0 12px 0;
color: #333;
}
.attachment-list {
display: flex;
flex-direction: column;
gap: 8px;
}
.attachment-item {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 12px;
border: 1px solid #e8e8e8;
border-radius: 4px;
background-color: #f9f9f9;
}
.attachment-name {
flex: 1;
}
.attachment-size {
font-size: 12px;
color: #999;
margin-right: 8px;
}
</style>

View File

@@ -1,479 +0,0 @@
<template>
<div>
<page-header title="防疫机构管理"/>
<!-- 搜索和操作栏 -->
<a-card style="margin-bottom: 16px;">
<div style="display: flex; flex-wrap: wrap; gap: 16px; align-items: center;">
<a-input v-model:value="searchKeyword" placeholder="输入机构名称或负责人" style="width: 250px;">
<template #prefix>
<span class="iconfont icon-sousuo"></span>
</template>
</a-input>
<a-select v-model:value="statusFilter" placeholder="机构状态" style="width: 120px;">
<a-select-option value="">全部</a-select-option>
<a-select-option value="active">启用</a-select-option>
<a-select-option value="inactive">禁用</a-select-option>
</a-select>
<a-button type="primary" @click="handleSearch" style="margin-left: auto;">
<span class="iconfont icon-sousuo"></span> 搜索
</a-button>
<a-button type="default" @click="handleReset">重置</a-button>
<a-button type="primary" danger @click="handleAddAgency">
<span class="iconfont icon-tianjia"></span> 新增机构
</a-button>
</div>
</a-card>
<!-- 机构列表 -->
<a-card>
<a-table
:columns="columns"
:data-source="agenciesData"
:pagination="pagination"
row-key="id"
:row-selection="{ selectedRowKeys, onChange: onSelectChange }"
:scroll="{ x: 'max-content' }"
>
<!-- 状态列 -->
<template #bodyCell:status="{ record }">
<a-tag :color="getStatusColor(record.status)">{{ getStatusText(record.status) }}</a-tag>
</template>
<!-- 操作列 -->
<template #bodyCell:action="{ record }">
<div style="display: flex; gap: 8px;">
<a-button size="small" @click="handleView(record)">查看</a-button>
<a-button size="small" type="primary" @click="handleEdit(record)">编辑</a-button>
<a-button size="small" danger @click="handleDelete(record.id)">删除</a-button>
<a-button size="small" @click="handleToggleStatus(record)">
{{ record.status === 'active' ? '禁用' : '启用' }}
</a-button>
</div>
</template>
</a-table>
</a-card>
<!-- 新增/编辑机构模态框 -->
<a-modal
v-model:open="isAddEditModalOpen"
:title="isEditMode ? '编辑防疫机构' : '新增防疫机构'"
:footer="null"
width={700}
>
<a-form
:model="currentAgency"
layout="vertical"
>
<a-form-item
label="机构名称"
name="name"
:rules="[{ required: true, message: '请输入机构名称' }]"
>
<a-input v-model:value="currentAgency.name" placeholder="请输入机构名称" />
</a-form-item>
<a-form-item
label="负责人"
name="director"
:rules="[{ required: true, message: '请输入负责人姓名' }]"
>
<a-input v-model:value="currentAgency.director" placeholder="请输入负责人姓名" />
</a-form-item>
<a-form-item
label="联系电话"
name="phone"
:rules="[{ required: true, message: '请输入联系电话' }]"
>
<a-input v-model:value="currentAgency.phone" placeholder="请输入联系电话" />
</a-form-item>
<a-form-item
label="地址"
name="address"
:rules="[{ required: true, message: '请输入机构地址' }]"
>
<a-input v-model:value="currentAgency.address" placeholder="请输入机构地址" />
</a-form-item>
<a-form-item
label="邮箱"
name="email"
>
<a-input v-model:value="currentAgency.email" placeholder="请输入邮箱地址" />
</a-form-item>
<a-form-item
label="机构类型"
name="type"
:rules="[{ required: true, message: '请选择机构类型' }]"
>
<a-select v-model:value="currentAgency.type" placeholder="请选择机构类型">
<a-select-option value="center">中心防疫站</a-select-option>
<a-select-option value="branch">分站</a-select-option>
<a-select-option value="mobile">流动防疫站</a-select-option>
</a-select>
</a-form-item>
<a-form-item
label="简介"
name="description"
>
<a-textarea v-model:value="currentAgency.description" placeholder="请输入机构简介" :rows="4" />
</a-form-item>
</a-form>
<div style="text-align: right; margin-top: 20px;">
<a-button @click="handleCancel">取消</a-button>
<a-button type="primary" @click="handleSave">确定</a-button>
</div>
</a-modal>
<!-- 查看机构详情模态框 -->
<a-modal
v-model:open="isViewModalOpen"
title="查看防疫机构详情"
:footer="null"
width={800}
>
<div v-if="viewAgency" class="agency-detail">
<div class="detail-row">
<span class="detail-label">机构名称</span>
<span class="detail-value">{{ viewAgency.name }}</span>
</div>
<div class="detail-row">
<span class="detail-label">负责人</span>
<span class="detail-value">{{ viewAgency.director }}</span>
</div>
<div class="detail-row">
<span class="detail-label">联系电话</span>
<span class="detail-value">{{ viewAgency.phone }}</span>
</div>
<div class="detail-row">
<span class="detail-label">地址</span>
<span class="detail-value">{{ viewAgency.address }}</span>
</div>
<div class="detail-row">
<span class="detail-label">邮箱</span>
<span class="detail-value">{{ viewAgency.email }}</span>
</div>
<div class="detail-row">
<span class="detail-label">机构类型</span>
<span class="detail-value">{{ getAgencyTypeText(viewAgency.type) }}</span>
</div>
<div class="detail-row">
<span class="detail-label">状态</span>
<a-tag :color="getStatusColor(viewAgency.status)">{{ getStatusText(viewAgency.status) }}</a-tag>
</div>
<div class="detail-row">
<span class="detail-label">成立时间</span>
<span class="detail-value">{{ viewAgency.establishmentDate }}</span>
</div>
<div class="detail-row">
<span class="detail-label">简介</span>
<span class="detail-value">{{ viewAgency.description }}</span>
</div>
</div>
<div style="text-align: right; margin-top: 20px;">
<a-button @click="closeViewModal">关闭</a-button>
</div>
</a-modal>
</div>
</template>
<script setup>
import { ref, reactive } from 'vue'
import { message } from 'ant-design-vue'
import PageHeader from '@/layout/PageHeader.vue'
// 搜索条件
const searchKeyword = ref('')
const statusFilter = ref('')
// 表格数据
const selectedRowKeys = ref([])
const agenciesData = ref([
{
id: '1',
name: '中心动物防疫站',
director: '张三',
phone: '13800138001',
address: '市南区健康路100号',
email: 'center@animalhealth.gov.cn',
type: 'center',
status: 'active',
establishmentDate: '2010-01-15',
description: '负责全市动物防疫工作的统筹管理和技术指导'
},
{
id: '2',
name: '东区动物防疫分站',
director: '李四',
phone: '13800138002',
address: '市东区防疫路50号',
email: 'east@animalhealth.gov.cn',
type: 'branch',
status: 'active',
establishmentDate: '2012-05-20',
description: '负责东区范围内的动物防疫工作'
},
{
id: '3',
name: '西区动物防疫分站',
director: '王五',
phone: '13800138003',
address: '市西区健康大道200号',
email: 'west@animalhealth.gov.cn',
type: 'branch',
status: 'active',
establishmentDate: '2013-03-10',
description: '负责西区范围内的动物防疫工作'
},
{
id: '4',
name: '北区动物防疫分站',
director: '赵六',
phone: '13800138004',
address: '市北区安全路88号',
email: 'north@animalhealth.gov.cn',
type: 'branch',
status: 'active',
establishmentDate: '2014-07-05',
description: '负责北区范围内的动物防疫工作'
},
{
id: '5',
name: '南区动物防疫分站',
director: '钱七',
phone: '13800138005',
address: '市南区健康路66号',
email: 'south@animalhealth.gov.cn',
type: 'branch',
status: 'active',
establishmentDate: '2015-02-28',
description: '负责南区范围内的动物防疫工作'
},
{
id: '6',
name: '流动防疫队',
director: '孙八',
phone: '13800138006',
address: '市中区应急中心',
email: 'mobile@animalhealth.gov.cn',
type: 'mobile',
status: 'active',
establishmentDate: '2016-09-15',
description: '负责偏远地区和突发事件的动物防疫工作'
}
])
// 分页配置
const pagination = {
current: 1,
pageSize: 10,
total: 6,
showSizeChanger: true,
showQuickJumper: true,
pageSizeOptions: ['10', '20', '50', '100']
}
// 模态框状态
const isAddEditModalOpen = ref(false)
const isEditMode = ref(false)
const isViewModalOpen = ref(false)
// 当前编辑/查看的机构
const currentAgency = reactive({
id: '',
name: '',
director: '',
phone: '',
address: '',
email: '',
type: '',
status: 'active',
description: ''
})
const viewAgency = ref(null)
// 表格列定义
const columns = [
{
title: '机构名称',
dataIndex: 'name',
key: 'name'
},
{
title: '负责人',
dataIndex: 'director',
key: 'director',
width: 100
},
{
title: '联系电话',
dataIndex: 'phone',
key: 'phone',
width: 120
},
{
title: '机构类型',
dataIndex: 'type',
key: 'type',
width: 100,
customRender: ({ text }) => getAgencyTypeText(text)
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
width: 80
},
{
title: '成立时间',
dataIndex: 'establishmentDate',
key: 'establishmentDate',
width: 120
},
{
title: '操作',
key: 'action',
width: 180,
fixed: 'right'
}
]
// 获取机构类型文本
const getAgencyTypeText = (type) => {
const typeMap = {
'center': '中心防疫站',
'branch': '分站',
'mobile': '流动防疫站'
}
return typeMap[type] || type
}
// 获取状态文本
const getStatusText = (status) => {
const statusMap = {
'active': '启用',
'inactive': '禁用'
}
return statusMap[status] || status
}
// 获取状态颜色
const getStatusColor = (status) => {
const colorMap = {
'active': 'green',
'inactive': 'red'
}
return colorMap[status] || 'blue'
}
// 行选择变化
const onSelectChange = (newSelectedRowKeys) => {
selectedRowKeys.value = newSelectedRowKeys
}
// 搜索
const handleSearch = () => {
// 实际项目中这里应该调用API进行搜索
message.success('搜索成功')
}
// 重置
const handleReset = () => {
searchKeyword.value = ''
statusFilter.value = ''
}
// 新增机构
const handleAddAgency = () => {
isEditMode.value = false
Object.assign(currentAgency, {
id: '',
name: '',
director: '',
phone: '',
address: '',
email: '',
type: '',
status: 'active',
description: ''
})
isAddEditModalOpen.value = true
}
// 编辑机构
const handleEdit = (record) => {
isEditMode.value = true
Object.assign(currentAgency, { ...record })
isAddEditModalOpen.value = true
}
// 查看机构
const handleView = (record) => {
viewAgency.value = { ...record }
isViewModalOpen.value = true
}
// 删除机构
const handleDelete = (id) => {
// 实际项目中这里应该调用API进行删除
message.success('删除成功')
}
// 切换状态
const handleToggleStatus = (record) => {
// 实际项目中这里应该调用API切换状态
message.success(`状态已切换为${record.status === 'active' ? '禁用' : '启用'}`)
}
// 保存机构
const handleSave = () => {
// 实际项目中这里应该调用API保存数据
message.success(isEditMode.value ? '编辑成功' : '新增成功')
isAddEditModalOpen.value = false
}
// 取消
const handleCancel = () => {
isAddEditModalOpen.value = false
}
// 关闭查看模态框
const closeViewModal = () => {
isViewModalOpen.value = false
viewAgency.value = null
}
</script>
<style scoped>
.agency-detail {
padding: 20px;
}
.detail-row {
margin-bottom: 16px;
display: flex;
align-items: flex-start;
}
.detail-label {
font-weight: 600;
width: 100px;
flex-shrink: 0;
}
.detail-value {
flex: 1;
word-break: break-word;
}
</style>

View File

@@ -31,7 +31,7 @@
v-model:value="dateRange"
style="width: 300px;"
format="YYYY-MM-DD"
placeholder={['申报日期', '申报日期']}
:placeholder="['申报日期', '申报日期']"
/>
<a-input v-model:value="quarantineOfficer" placeholder="检疫员" style="width: 150px;">

View File

@@ -124,6 +124,8 @@
<script>
import { ref, reactive, onMounted } from 'vue';
import { PlusOutlined } from '@ant-design/icons-vue';
import { message } from 'ant-design-vue';
import api from '@/utils/api';
export default {
name: 'SmartCollar',
@@ -212,49 +214,30 @@ export default {
const detailModalVisible = ref(false);
const detailData = reactive({});
// 模拟数据
const mockData = [
{
id: '1',
collarId: 'CL001',
name: '智能项圈001',
status: 'active',
battery: 85,
createdAt: '2023-09-15 10:00:00',
updatedAt: '2023-09-20 14:30:00',
remark: '用于示范的项圈'
},
{
id: '2',
collarId: 'CL002',
name: '智能项圈002',
status: 'inactive',
battery: 100,
createdAt: '2023-09-16 11:20:00',
updatedAt: '2023-09-16 11:20:00',
remark: ''
},
{
id: '3',
collarId: 'CL003',
name: '智能项圈003',
status: 'maintenance',
battery: 20,
createdAt: '2023-09-10 09:15:00',
updatedAt: '2023-09-21 16:45:00',
remark: '电池需要更换'
}
];
// 移除模拟数据使用真实API
// 获取数据
const fetchData = () => {
const fetchData = async () => {
loading.value = true;
// 模拟API请求
setTimeout(() => {
dataSource.value = mockData;
pagination.total = mockData.length;
try {
const params = {
page: pagination.current,
pageSize: pagination.pageSize,
collarId: searchForm.collarId,
status: searchForm.status
};
const response = await api.government.collars.getList(params);
if (response.success) {
dataSource.value = response.data;
pagination.total = response.total;
} else {
console.error('获取智能项圈列表失败:', response.message);
}
} catch (error) {
console.error('获取智能项圈列表失败:', error);
} finally {
loading.value = false;
}, 500);
}
};
// 状态相关
@@ -318,34 +301,41 @@ export default {
modalVisible.value = true;
};
const handleModalOk = () => {
formRef.value.validate().then(() => {
const handleModalOk = async () => {
formRef.value.validate().then(async () => {
modalLoading.value = true;
// 模拟API请求
setTimeout(() => {
try {
const requestData = {
collarId: formData.collarId,
name: formData.name,
status: formData.status,
battery: formData.battery,
remark: formData.remark
};
let response;
if (formData.id) {
// 编辑
const index = mockData.findIndex(item => item.id === formData.id);
if (index !== -1) {
mockData[index] = {
...mockData[index],
...formData,
updatedAt: new Date().toLocaleString()
};
}
response = await api.government.collars.update(formData.id, requestData);
} else {
// 添加
mockData.push({
id: String(mockData.length + 1),
...formData,
createdAt: new Date().toLocaleString(),
updatedAt: new Date().toLocaleString()
});
response = await api.government.collars.create(requestData);
}
if (response.success) {
modalLoading.value = false;
modalVisible.value = false;
fetchData();
message.success(response.message || '操作成功');
} else {
message.error(response.message || '操作失败');
}
} catch (error) {
console.error('操作失败:', error);
message.error('操作失败,请重试');
} finally {
modalLoading.value = false;
modalVisible.value = false;
fetchData();
}, 500);
}
});
};
@@ -364,17 +354,22 @@ export default {
};
// 删除
const handleDelete = (record) => {
const handleDelete = async (record) => {
loading.value = true;
// 模拟API请求
setTimeout(() => {
const index = mockData.findIndex(item => item.id === record.id);
if (index !== -1) {
mockData.splice(index, 1);
try {
const response = await api.government.collars.delete(record.id);
if (response.success) {
fetchData();
message.success(response.message || '删除成功');
} else {
message.error(response.message || '删除失败');
}
} catch (error) {
console.error('删除失败:', error);
message.error('删除失败,请重试');
} finally {
loading.value = false;
fetchData();
}, 500);
}
};
onMounted(() => {

View File

@@ -124,6 +124,7 @@
<script>
import { ref, reactive, onMounted } from 'vue';
import { PlusOutlined } from '@ant-design/icons-vue';
import api from '@/utils/api';
export default {
name: 'SmartEarmark',
@@ -212,49 +213,26 @@ export default {
const detailModalVisible = ref(false);
const detailData = reactive({});
// 模拟数据
const mockData = [
{
id: '1',
earmarkId: 'EM001',
name: '智能耳标001',
status: 'active',
battery: 90,
createdAt: '2023-09-15 10:00:00',
updatedAt: '2023-09-20 14:30:00',
remark: '用于示范的耳标'
},
{
id: '2',
earmarkId: 'EM002',
name: '智能耳标002',
status: 'inactive',
battery: 100,
createdAt: '2023-09-16 11:20:00',
updatedAt: '2023-09-16 11:20:00',
remark: ''
},
{
id: '3',
earmarkId: 'EM003',
name: '智能耳标003',
status: 'maintenance',
battery: 15,
createdAt: '2023-09-10 09:15:00',
updatedAt: '2023-09-21 16:45:00',
remark: '电池需要更换'
}
];
// 获取数据
const fetchData = () => {
const fetchData = async () => {
loading.value = true;
// 模拟API请求
setTimeout(() => {
dataSource.value = mockData;
pagination.total = mockData.length;
try {
const params = {
page: pagination.current,
pageSize: pagination.pageSize,
earmarkId: searchForm.earmarkId,
status: searchForm.status
};
const response = await api.government.earmarks.getList(params);
if (response.success) {
dataSource.value = response.data;
pagination.total = response.total;
}
} catch (error) {
console.error('获取智能耳标列表失败:', error);
} finally {
loading.value = false;
}, 500);
}
};
// 状态相关
@@ -318,35 +296,34 @@ export default {
modalVisible.value = true;
};
const handleModalOk = () => {
formRef.value.validate().then(() => {
const handleModalOk = async () => {
try {
await formRef.value.validate();
modalLoading.value = true;
// 模拟API请求
setTimeout(() => {
if (formData.id) {
// 编辑
const index = mockData.findIndex(item => item.id === formData.id);
if (index !== -1) {
mockData[index] = {
...mockData[index],
...formData,
updatedAt: new Date().toLocaleString()
};
}
} else {
// 添加
mockData.push({
id: String(mockData.length + 1),
...formData,
createdAt: new Date().toLocaleString(),
updatedAt: new Date().toLocaleString()
});
}
modalLoading.value = false;
modalVisible.value = false;
fetchData();
}, 500);
});
const requestData = {
earmarkId: formData.earmarkId,
name: formData.name,
status: formData.status,
battery: formData.battery,
remark: formData.remark
};
if (formData.id) {
// 编辑
await api.government.earmarks.update(formData.id, requestData);
} else {
// 添加
await api.government.earmarks.create(requestData);
}
modalLoading.value = false;
modalVisible.value = false;
fetchData();
} catch (error) {
console.error('保存智能耳标失败:', error);
modalLoading.value = false;
}
};
const handleModalCancel = () => {
@@ -364,17 +341,16 @@ export default {
};
// 删除
const handleDelete = (record) => {
loading.value = true;
// 模拟API请求
setTimeout(() => {
const index = mockData.findIndex(item => item.id === record.id);
if (index !== -1) {
mockData.splice(index, 1);
}
loading.value = false;
const handleDelete = async (record) => {
try {
loading.value = true;
await api.government.earmarks.delete(record.id);
fetchData();
}, 500);
} catch (error) {
console.error('删除智能耳标失败:', error);
} finally {
loading.value = false;
}
};
onMounted(() => {

View File

@@ -124,6 +124,27 @@
<script>
import { ref, reactive, onMounted } from 'vue';
import { PlusOutlined } from '@ant-design/icons-vue';
import api from '../../../utils/api';
// 状态映射函数:前端状态 -> 后端状态
const frontToBackStatus = (status) => {
const statusMap = {
online: 'active',
offline: 'inactive',
maintenance: 'maintenance'
};
return statusMap[status] || status;
};
// 状态映射函数:后端状态 -> 前端状态
const backToFrontStatus = (status) => {
const statusMap = {
active: 'online',
inactive: 'offline',
maintenance: 'maintenance'
};
return statusMap[status] || status;
};
export default {
name: 'SmartHost',
@@ -212,49 +233,39 @@ export default {
const detailModalVisible = ref(false);
const detailData = reactive({});
// 模拟数据
const mockData = [
{
id: '1',
hostId: 'HOST001',
name: '智能主机001',
ipAddress: '192.168.1.100',
status: 'online',
createdAt: '2023-09-15 10:00:00',
updatedAt: '2023-09-20 14:30:00',
remark: '中心控制主机'
},
{
id: '2',
hostId: 'HOST002',
name: '智能主机002',
ipAddress: '192.168.1.101',
status: 'offline',
createdAt: '2023-09-16 11:20:00',
updatedAt: '2023-09-16 11:20:00',
remark: ''
},
{
id: '3',
hostId: 'HOST003',
name: '智能主机003',
ipAddress: '192.168.1.102',
status: 'maintenance',
createdAt: '2023-09-10 09:15:00',
updatedAt: '2023-09-21 16:45:00',
remark: '系统升级中'
}
];
// 获取数据
const fetchData = () => {
const fetchData = async () => {
loading.value = true;
// 模拟API请求
setTimeout(() => {
dataSource.value = mockData;
pagination.total = mockData.length;
try {
const params = {
page: pagination.current,
pageSize: pagination.pageSize,
hostId: searchForm.hostId || undefined,
status: searchForm.status ? frontToBackStatus(searchForm.status) : undefined
};
const response = await api.government.smartHosts.getList(params);
if (response.success) {
// 将后端返回的状态转换为前端使用的状态
dataSource.value = response.data.map(item => ({
...item,
status: backToFrontStatus(item.status)
}));
pagination.total = response.total;
} else {
throw new Error(response.message || '获取数据失败');
}
} catch (error) {
console.error('获取智能主机数据失败:', error);
dataSource.value = [];
pagination.total = 0;
// 可以在这里添加错误提示
} finally {
loading.value = false;
}, 500);
}
};
// 状态相关
@@ -318,34 +329,38 @@ export default {
modalVisible.value = true;
};
const handleModalOk = () => {
formRef.value.validate().then(() => {
const handleModalOk = async () => {
formRef.value.validate().then(async () => {
modalLoading.value = true;
// 模拟API请求
setTimeout(() => {
try {
// 准备请求数据,将前端状态转换为后端状态
const requestData = {
...formData,
status: frontToBackStatus(formData.status)
};
let response;
if (formData.id) {
// 编辑
const index = mockData.findIndex(item => item.id === formData.id);
if (index !== -1) {
mockData[index] = {
...mockData[index],
...formData,
updatedAt: new Date().toLocaleString()
};
}
response = await api.government.smartHosts.update(formData.id, requestData);
} else {
// 添加
mockData.push({
id: String(mockData.length + 1),
...formData,
createdAt: new Date().toLocaleString(),
updatedAt: new Date().toLocaleString()
});
response = await api.government.smartHosts.create(requestData);
}
modalLoading.value = false;
if (!response.success) {
throw new Error(response.message || '操作失败');
}
modalVisible.value = false;
fetchData();
}, 500);
// 可以在这里添加成功提示
} catch (error) {
console.error('操作失败:', error);
// 可以在这里添加错误提示
} finally {
modalLoading.value = false;
}
});
};
@@ -364,17 +379,23 @@ export default {
};
// 删除
const handleDelete = (record) => {
const handleDelete = async (record) => {
loading.value = true;
// 模拟API请求
setTimeout(() => {
const index = mockData.findIndex(item => item.id === record.id);
if (index !== -1) {
mockData.splice(index, 1);
try {
const response = await api.government.smartHosts.delete(record.id);
if (!response.success) {
throw new Error(response.message || '删除失败');
}
loading.value = false;
fetchData();
}, 500);
// 可以在这里添加成功提示
} catch (error) {
console.error('删除智能主机失败:', error);
// 可以在这里添加错误提示
} finally {
loading.value = false;
}
};
onMounted(() => {

View File

@@ -0,0 +1,86 @@
// 测试前端认证修复效果的脚本
import axios from 'axios';
import fs from 'fs';
import path from 'path';
// 配置
const BASE_URL = 'http://localhost:5352/api';
const USERNAME = 'admin';
const PASSWORD = '123456';
// 测试函数
async function runTest() {
console.log('===== 开始测试智慧耳标页面认证问题 =====');
try {
// 1. 尝试登录获取token
console.log('1. 尝试登录...');
const loginResponse = await axios.post(`${BASE_URL}/auth/login`, {
username: USERNAME,
password: PASSWORD
});
if (loginResponse.data && loginResponse.data.token) {
const token = loginResponse.data.token;
console.log('登录成功获取到token:', token);
// 2. 使用获取的token尝试访问智能耳标API
console.log('2. 使用token访问智能耳标API...');
const headers = { 'Authorization': `Bearer ${token}` };
const smartEarmarkResponse = await axios.get(`${BASE_URL}/smart-earmark`, { headers });
console.log('智能耳标API访问成功返回状态:', smartEarmarkResponse.status);
console.log('返回数据示例:', JSON.stringify(smartEarmarkResponse.data.slice(0, 1), null, 2));
console.log('\n===== 测试成功!智慧耳标页面认证问题已修复 =====');
} else {
console.error('登录失败未获取到token:', loginResponse.data);
// 如果后端使用模拟数据我们也创建一个模拟token来测试
console.log('\n3. 尝试使用模拟token访问API适用于前端模拟登录场景...');
const mockToken = 'mock-jwt-token-' + Date.now();
const headers = { 'Authorization': `Bearer ${mockToken}` };
try {
const smartEarmarkResponse = await axios.get(`${BASE_URL}/smart-earmark`, {
headers,
timeout: 5000
});
console.log('使用模拟token访问成功状态:', smartEarmarkResponse.status);
} catch (error) {
console.error('使用模拟token访问失败:', error.code || error.message);
if (error.response) {
console.error('错误详情:', error.response.status, error.response.data);
}
// 记录问题以便后续分析
const errorInfo = {
timestamp: new Date().toISOString(),
error: error.message,
response: error.response ? {
status: error.response.status,
data: error.response.data
} : null
};
fs.writeFileSync(
path.join(__dirname, 'auth_error.log'),
JSON.stringify(errorInfo, null, 2) + '\n',
{ flag: 'a' }
);
}
}
} catch (error) {
console.error('测试过程中发生错误:', error.message);
if (error.response) {
console.error('错误状态码:', error.response.status);
console.error('错误详情:', error.response.data);
} else if (error.request) {
console.error('未收到响应:', error.request);
console.error('请检查后端服务是否正常运行在端口5352');
}
}
}
// 运行测试
runTest();

View File

@@ -0,0 +1,66 @@
// 简化的测试脚本专门验证智慧耳标API访问
import axios from 'axios';
// 配置
const API_BASE_URL = 'http://localhost:5352/api';
const TEST_USERNAME = 'admin';
const TEST_PASSWORD = '123456';
// 创建axios实例
const apiClient = axios.create({
baseURL: API_BASE_URL,
timeout: 10000,
headers: {
'Content-Type': 'application/json',
},
});
// 测试函数
async function testEarmarkApi() {
try {
console.log('===== 开始测试智慧耳标API =====');
// 1. 尝试登录获取真实token
console.log('1. 登录获取token...');
const loginResponse = await apiClient.post('/auth/login', {
username: TEST_USERNAME,
password: TEST_PASSWORD
});
if (loginResponse.status === 200 && loginResponse.data.code === 200) {
const token = loginResponse.data.data.token;
console.log('登录成功获取到token');
// 2. 使用获取的token访问智慧耳标API
console.log('2. 使用token访问智慧耳标API...');
apiClient.defaults.headers.common['Authorization'] = `Bearer ${token}`;
const earmarkResponse = await apiClient.get('/smart-earmark');
if (earmarkResponse.status === 200) {
console.log('智慧耳标API访问成功');
console.log('响应状态码:', earmarkResponse.status);
console.log('数据总量:', earmarkResponse.data.data?.length || '无数据');
console.log('\n===== 测试成功 =====');
console.log('修复总结:');
console.log('1. 前端修复了api.js中缺失的message组件导入');
console.log('2. 前端修复了authStore中认证状态判断问题');
console.log('3. 后端修复了auth.js中JWT验证相关的导入问题');
console.log('4. 后端:修复了重复声明的变量问题');
console.log('\n结论点击智慧耳标页面自动退出到登录页的问题已解决');
} else {
console.error('智慧耳标API访问失败状态码:', earmarkResponse.status);
}
} else {
console.error('登录失败:', loginResponse.data?.message || '未知错误');
}
} catch (error) {
console.error('测试过程中发生错误:', error.message);
if (error.response) {
console.error('错误详情:', error.response.status, error.response.data);
}
}
}
// 运行测试
testEarmarkApi();